I awoke one day last week with this comic idea percolating in my brain. Too bad I'm lousy at drawing.

My old blog - come see my new one
$('input#user_email').focus();
$('#sign-in-modal-form').find('input:not(:hidden):first').focus();
someobject.methods.
irb(main):001:0> a = Array.new => [] irb(main):002:0> a.methods.sort.collect {|m| puts "#{m} defined by #{a.method(m).owner}"} & defined by Array * defined by Array + defined by Array - defined by Array << defined by Array <=> defined by Array == defined by Array === defined by Kernel =~ defined by Kernel [] defined by Array []= defined by Array __id__ defined by Kernel __send__ defined by Kernel all? defined by Enumerable any? defined by Enumerable assoc defined by Array at defined by Array choice defined by Array class defined by Kernel clear defined by Array clone defined by Kernel collect defined by Array collect! defined by Array combination defined by Array compact defined by Array compact! defined by Array concat defined by Array count defined by Array cycle defined by Array delete defined by Array delete_at defined by Array delete_if defined by Array detect defined by Enumerable display defined by Kernel drop defined by Array drop_while defined by Array dup defined by Kernel each defined by Array each_cons defined by Enumerable each_index defined by Array each_slice defined by Enumerable each_with_index defined by Enumerable empty? defined by Array entries defined by Enumerable enum_cons defined by Enumerable enum_for defined by Kernel enum_slice defined by Enumerable enum_with_index defined by Enumerable eql? defined by Array equal? defined by Kernel extend defined by Kernel fetch defined by Array fill defined by Array find defined by Enumerable find_all defined by Enumerable find_index defined by Array first defined by Array flatten defined by Array flatten! defined by Array freeze defined by Kernel frozen? defined by Array grep defined by Enumerable group_by defined by Enumerable hash defined by Array id defined by Kernel include? defined by Array index defined by Array indexes defined by Array indices defined by Array inject defined by Enumerable insert defined by Array inspect defined by Array instance_eval defined by Kernel instance_exec defined by Kernel instance_of? defined by Kernel instance_variable_defined? defined by Kernel instance_variable_get defined by Kernel instance_variable_set defined by Kernel instance_variables defined by Kernel is_a? defined by Kernel join defined by Array kind_of? defined by Kernel last defined by Array length defined by Array map defined by Array map! defined by Array max defined by Enumerable max_by defined by Enumerable member? defined by Enumerable method defined by Kernel methods defined by Kernel min defined by Enumerable min_by defined by Enumerable minmax defined by Enumerable minmax_by defined by Enumerable nil? defined by Kernel nitems defined by Array none? defined by Enumerable object_id defined by Kernel one? defined by Enumerable pack defined by Array partition defined by Enumerable permutation defined by Array pop defined by Array private_methods defined by Kernel product defined by Array protected_methods defined by Kernel public_methods defined by Kernel push defined by Array rassoc defined by Array reduce defined by Enumerable reject defined by Array reject! defined by Array replace defined by Array respond_to? defined by Kernel reverse defined by Array reverse! defined by Array reverse_each defined by Array rindex defined by Array select defined by Array send defined by Kernel shift defined by Array shuffle defined by Array shuffle! defined by Array singleton_methods defined by Kernel size defined by Array slice defined by Array slice! defined by Array sort defined by Array sort! defined by Array sort_by defined by Enumerable taint defined by Kernel tainted? defined by Kernel take defined by Array take_while defined by Array tap defined by Kernel to_a defined by Array to_ary defined by Array to_enum defined by Kernel to_s defined by Array transpose defined by Array type defined by Kernel uniq defined by Array uniq! defined by Array unshift defined by Array untaint defined by Kernel values_at defined by Array zip defined by Array | defined by Array
Class.ancestors # [Class, Module, Object, Kernel] Module.ancestors # [Module, Object, Kernel] Object.ancestors # [Object, Kernel] Kernel.ancestors # uninitialized constant Kernel
So she asked this kid who knew everything. Irwin. “Irwin, what’s the plural for ox?”
“Ox. Oxen. The farmer used his oxen.”
“Brian?”
“What?”
“Brian, what’s the plural for box?”
“Boxen. I bought 2 boxen of doughnuts.”
“No, Brian, no. Let’s try another one. Irwin, what’s the plural for goose?”
“Geese. I saw a flock of geese.”
“Brian?”
[Exasperated laughing]“Wha-a-at?”
“What’s the plural for moose?”
“Moosen! I saw a flock of MOOSEN! There were many of ‘em. Many much moosen. Out in the woods…in the wood-es…in the woodsen. The meese want the food in the woodesen…food is the eatenesen…the meese want the food in the woodesenes…food in the woodesenes.”
Every noun has a gender, and there is no sense or system in the distribution; so the gender of each must be learned separately and by heart. There is no other way. To do this one has to have a memory like a memorandum-book. In German, a young lady has no sex, while a turnip has. Think what overwrought reverence that shows for the turnip, and what callous disrespect for the girl. See how it looks in print -- I translate this from a conversation in one of the best of the German Sunday-school books:
Gretchen: "Wilhelm, where is the turnip?"
Wilhelm: "She has gone to the kitchen."
Gretchen: "Where is the accomplished and beautiful English maiden?"
Wilhelm: "It has gone to the opera."
Esperanto is much easier to learn than other languages because:
- The different letters are always pronounced in the same way and every letter in a word is pronounced. Therefore there are no difficulties with spelling and pronunciation, as one knows that the penultimate syllable is always stressed.
- The grammar is simple, logical and without exceptions. The grammatical exceptions are often what make it so difficult to learn a new language.
- Most of the words in Esperanto are international and are found in languages around the world.
- It is easy to make new words with prefixes or suffixes. Thus, if one learns one word, ten or more usually come as part of the package.
...Programming languages are ways to express human thought... Machines do not care whether programs are structured well; they just execute them bit by bit. Structured programming is not for machines, but for humans... So to design a human-oriented langauge, Ruby, I followed the Principle of Least Surprise. I consider that everything that surprises me less is good. As a result I feel a natural feeling, even a kind of joy, when programming in Ruby.
cachedFile('foo.png'); cachedFile('subdirectory/bar.png','class="buz"')
$GLOBAL_cachedFile_cache = null; function cachedFile($name, $attr=null){ global $GLOBAL_cachedFile_cache; if (!isset($GLOBAL_cachedFile_cache[$name])){ $root = $_SERVER['DOCUMENT_ROOT']; $filetype = substr($name,strripos($name,'.')+1); /* Configuration options */ $imgpath = '/images/'; $csspath = '/stylesheets/'; $jspath = '/scripts/'; switch ($filetype){ case 'css': $output = '<link rel="stylesheet" type="text/css" href="/includes/'; $output .= $name; $output .= '?' . filemtime($root . $csspath . $name) . '" '; if($attr){ $output .= $attr . ' '; } $output .= '/>' . "\n"; break; case 'js': $output = '<script type="text/javascript" src="/includes/'; $output .= $name; $output .= '?' . filemtime($root . $jspath . $name) . '"'; $output .= '</script>' . "\n"; break; case 'jpg': case 'gif': case 'png': //This code will get run in any of the three cases above $output = '<img src="' . $imgpath . $name; $output .= '?' . filemtime($root . $imgpath . $name) . '"'; $imgsize = getimagesize($root . $imgpath . $name); $output .= ' ' . $imgsize[3]; if($attr){ $output .= ' ' . $attr; } $output .= ' />'; break; } $GLOBAL_cachedFile_cache[$name] = $output; } echo $GLOBAL_cachedFile_cache[$name]; }
Using asset timestamps
By default, Rails appends asset‘s timestamps to all asset paths[1]. This allows you to set a cache-expiration date for the asset far into the future, but still be able to instantly invalidate it by simply updating the file (and hence updating the timestamp, which then updates the URL as the timestamp is part of that, which in turn busts the cache).
It‘s the responsibility of the web server you use to set the far-future expiration date on cache assets that you need to take advantage of this feature. Here‘s an example for Apache:
# Asset Expiration
ExpiresActive On
<filesmatch "\.(ico|gif|jpe?g|png|js|css)$">
ExpiresDefault "access plus 1 year"
</FilesMatch>
If you look at a the source for a Rails page, you'll see what they mean: the path to a stylesheet might be "/stylesheets/scaffold.css?1268228124", where the numbers at the end are the timestamp when the file was last updated.
So it should work like this:
1. The browser says 'give me this page'
2. The server says 'here, and by the way, this stylesheet called scaffold.css?1268228124 can be cached for a year - it's not gonna change.'
3. On reloads, the browser says 'I'm not asking for that css file, because my local copy is still good.'
4. A month later, you edit and save the file, which changes the timestamp, which means that the file is no longer called scaffold.css?1268228124 because the numbers change.
5. When the browser sees that, it says 'I've never seen that file! Give me a copy, please.' The cache is 'busted.'
In addition, Microsoft is requiring phone makers to keep basic elements of its user interface, including a physical button to start Web searches on Bing.
"Agile development keeps you focused on the task, and helps you always have something usable for the user so you get feedback."
What are some of the decisions you made that helped you move quickly?
"I knew it needed to have filter subscriptions, but at first it was simpler to just copy and past the entire Easylist into a file and use Unix regex tools to turn that into Javascript code [an array]… I didn't have any updating, but that was a quick and dirty way to have something that covered lots of websites at once. The next week, I had people reported ads that weren’t being blocked because I wasn’t updating Easylist, so I had to patch manually while I was trying to get filter subscriptions written.”
"When I put new features in, I tried to put them as opt-in experiments, using the Google Labs approach, where users that are geeky enough can try it out and give me feedback... Before that, I updated one night and went to bed, and the next morning I found people complaining in my comments that I blocked all images on all web sites. I kept getting those complaints for three days until everybody’s browser had gotten the version that fixed it, even though I had released 10 updates in that time. So now I do stuff in experiments first.”
Many developers feel like we can't publish something unless it's perfect. But clearly that attitude would have stopped you from succeeding here. How do you decide if something is "good enough for now?"
"It's a balance, but I think the idea that if I put something in there that's not perfect, it will hurt my reputation, that’s false, because that's what Microsoft has been doing forever. If you can put something out that's not perfect and get feedback, you're ahead of the person who hasn't released anything at all… I just look for the absolute simplest thing I can do that adds some functionality, then do that and release it… There are still a lot of things I want to do with AdBlock, but it's been useful for thousands of people even thought it's not perfect."
What did you learn during this process?
"This project has 500 times more users than anything I've ever built personally. I'm seeing how successful Agile can be if I just listen to users and do the thing that people are shouting for most. So I've validated the agile approach... combined with not ever sleeping."
"Also, I need to weigh how loud the user is with what percentage of the user base they represent. People were clamoring for a long time that I get a change log up and post my source code. I could have spent 4-5 hours doing that, but that would have been time I wasn’t blocking ads and fixing bugs, and most people don’t care about a change log. I actually wrote a priority list down – responding to user requests quickly was first, because people liked my fast customer service, then fixing ad reports, then fixing bug reports, then feature requests, and way down the list was administrative stuff like a change log. The list got more formalized when I finally got some breathing room, when the ads were being blocked and most of the bugs were fixed, so I created a Google Code project, where I can mark bugs with low or high priority and people can see my change log."
You used object orientation pretty heavily in your Javascript on this project. How did that help you?
"What makes it most useful for me in Javascript is that it simplifies the code; it's one level of complexity that's hidden. If an object has two methods exposed, I only have to remember those two methods, where with procedural code there are a bunch of methods in the global namespace that I have to keep up with. It's harder to do in Javascript because the syntax isn't as friendly as in something like Ruby, but once you learn the syntax, it's fine, and it makes your JS feel SO much nicer."
I had the chance to actually see you working for a bit, and the main thing that stood out to me was your fluid use of vim. It's almost like you're playing an instrument: you don't seem to think about the commands. You're just typing at a thousand miles an hour, and fully formatted code flows out and morphs on the screen, while split windows appear and disappear as needed. How did you attain such editor mastery, and how important is that to your career?
It's freaking wonderful to be fluent in one editor... I just think ‘I want an IF statement’ and the braces appear.
nextNumber(); //returns 1 nextNumber(); //returns 2 nextNumber(); //returns 3
var nextNumber = (function(){var i=1; return function() {return i++;}})();
<label for="helmet1color">Helmet 1 Color</label> <input id="helmet1color" name="helmet[1][color]"/> <label for="helmet1size">helmet 1 Size</label> <input id="helmet1size" name="helmet[1][size]"/>
function newHelmetInputs(i){ var inputSet = ''; inputSet += '<label for="helmet'+ i +'color">Helmet '+ i +' Color</label>' inputSet += '<input id="helmet'+ i +'color" name="helmet['+ i +'][color]"/>' inputSet += '<label for="helmet'+ i +'size">Helmet '+ i +' Size</label>' inputSet += '<input id="helmet'+ i +'size" name="helmet['+ i +'][size]"/>' return inputSet; }
i = nextNumber(); newInputs = newHelmetInputs(i);
$('#addhelmet').click(function(){ var i = nextNumber(); var newInputs = newHelmetInputs(i); $(this).before(newInputs); $('#helmet' + i + 'size').rules('add', {digits: true}); });
foreach ($_POST['helmet'] as $key=>$val){ $emailbody .= 'Helmet ' . $key . ' Color: ' . $val['color']; $emailbody .= 'Helmet ' . $key . ' Size: ' . $val['size']; $emailbody .= ' " }