Friday, March 26, 2010

the magical method_missing

In a typical method call, ruby will send the method name and arguments as a message to the object on which the method is being called. Most of the time, this will be an existing method, but if it is not, ruby calls a method called method_missing. The default behavior of method_missing is to throw a NoMethodError, which is exactly what you will get if you try, for example “i’m a string”.bglarbl.

Now, like any other method, method_missing can be overridden. A common rails use, and how I first discovered method_missing, is to simplify database queries and make them more readable. In rails, courtesy of method_missing, you can find_by_any_arbitrary_attribute without having to explicitly write the method to do so - as long as “any_arbitrary_attribute” is a column in your table, method_missing will take care of the rest.

You can do all sorts of tricky things here, which is both an expressive and dangerous feature (an increasingly recurring theme, I find, the more I learn about ruby.) You can do some really neat, useful things, like implement domain specific languages, but you can also do some really stupid, crazy-making things, like accidentally return “true” every time you call a non-existent, or even just a misspelled, method. Less dangerous, but still mildly annoying, is the fact that overriding method_missing can easily cause an object to respond to a method, while respond_to? will still return false for that method name, and the method will not show up in the array .methods returns. The big idea here is that overriding method_missing makes it really easy to make debugging really hard.

There are some ways to avoid making debugging a pain, however. One is to also override respond_to. Alternatively, depending on the expected usage, you might want to have method_missing dynamically construct a method, which will add the method to the methods list, and cause the expected behavior for respond_to. After some failed attempts at using define_method to do this, here’s one way to do this that works:

class MethodMissingTest
def method_missing(method_name)
method_source = "
def #{method_name}
puts 'added a method'
end
"
instance_eval(method_source)
send(method_name)
end
end

Now, you can make a new instance of MethodMissingTest - let's call it "feed_me_methods" and call any random string as a method on feed_me_methods, and feed_me_methods will now actually contain a method by that name, rather than just act like it does.

If you want to play some more with this, Sarah Allen has made a fun test-first xml tag-generator exercise. Cheers, and remember to method_missing responsibly!

Monday, March 22, 2010

infinite recursion

If you shovel an array into itself, it causes an infinite recursion. Watch me get dizzy in irb:

>> whoa = ["one", "two", "three"]
=> ["one", "two", "three"]
>> whoa << whoa
=> ["one", "two", "three", [...]]
>> whoa[3]
=> ["one", "two", "three", [...]]
>> whoa[3][3][3]
=> ["one", "two", "three", [...]]

by the set of all sets, ain't that cool.

RubyMine shortcuts for vim addicts

I'm a long-time vim user, but I've been using RubyMine recently for work. While I'm getting better and better at keeping the force of habit "3yy j p :wq" kind of nonsense out of my code, I still crave the lazyfingered awesomeness. Still, I've found some neat shortcuts in RubyMine that almost make up for my stubborn vim withdrawals.

Find
: RubyMine will find instances of a string in the current working file if you hit Ctrl-F, and will do a project-wide search if you hit Ctrl-Shift-F. I was particularly thrilled to discover Ctrl-Alt-B, which will search for the implementation of a method call, which is awesome, even if it doesn't work all the time...

Selecting: holding down control and hitting "." will select blocks of code, and subsequently hitting "." will work its way up the nesting tree selecting parents of parents. This is especially useful in Rspec, for selecting all contexts within a describe, or all examples in a context.

Yank/Put
: If you select multiple lines (using shift + arrows, or home/end) you can then move those lines up and down through your code in a block by holding control+shift and using up and down arrows. If you're lucky, and RubyMine is smart enough, sometimes this will actually skip over whole blocks of code that are indented.

Collapse and Search: speaking of Rspec, it can also be handy to collapse an entire spec by hitting Ctrl + Shift + "-", and then find instances of a keyword, which will expand only the instances of that word as they are found, leaving irrelevant tests collapsed.

Running Tests
: with your test open, you can press Ctrl-Shift-F11 to run all tests in a spec file. Or, pressing Alt-Shift-F11 will run only the current example (according to cursor location). If the Run window is open, Ctrl-4 will move your cursor to the Run window, and Ctrl-F5 will repeat the last test run.

Commenting out code: pressing Ctrl and "/" will comment out any currently selected lines, and will use the appropriate commenting convention according to the file extension.

Tuesday, March 16, 2010

block parameter scope weirdness

If the fact that external variables can be changed inside a block isn't strange enough for you, check out this example. What would you presume happens to "index" during the method call?

index = "watch this space"
my_array = ["base", "basket", "tennis", "goof"]
my_array.map {|index| index += "ball"}

It is a little alarming, but index is now equal to "goofball." This could make for some very unexpected and undesired behavior if you are not careful about what you are naming your block parameters. Dangerous!

Monday, March 8, 2010

ruby blocks

My first encounter with blocks in Ruby was in a koan, which was a black box kind of experience. I got stuck thinking of the block and the receiving function as two scopes, and under this assumption it seems that a block can return to a method *and* vice versa.

As it turns out, the block convention acts more like a run time code splicer, putting the block of code (before evaluating it) IN the method so, really, there is a shared scope between the two. It’s like you’re slipping the method a little note for what to put in the yield spot. But you can also pass the block a variable from inside the method, by writing yield(variable). It looks something like this:

def method_with_a_block
yield(parameter)
end

method_with_a_block {|parameter| parameter.do_stuff}

if you prefer a multiple-line version, you can replace the brackets with do/end (and I'll also throw in an extra surprise):
variable_outside_method = "foo"

method_with_a_block do |parameter_passed_to_block|
do_stuff_with(parameter_passed_to_block)
variable_outside_method += "bar
end

If you are accustomed to C-like languages, as I am, it can be surprising that our variable_outside_method is actually changed to "foobar" once the method call is resolved. This is because blocks have access to the context in which they are called.

Blocks are extremely expressive and useful for implementing a skeleton form of a task - in fact, enumerators in ruby (e.g. "each", "detect", "select") are implemented using blocks!

Thursday, March 4, 2010

ruby on rails

I'm learning Ruby on Rails. I've been spending this week with Sarah Allen, and have done a lot of work on my own from home. For ruby, I've been working the ruby koans from edgecase at github, and reading the ruby documentation and why's poignant guide to Ruby. I've also taken a fresh look at CSS, and have found the css tutorials at w3schools to be a huge help. For Rails, I've been using the Rails guides and Rails documentation for reference. That's it for now, much more to follow regarding the details of what I'm learning!