Monday, 14 July 2008

Ruby Methods Part 3 - The method_missing method

When you invoke a method on a class, the Ruby interpreter searches through the class hierarchy for a method of the given name, and if it fails it will invoke the method_missing method. This method is in the Kernel module, and so is accessible to the Object class, and hence to all objects. The method simply throws a NoMethodError. However subclasses can override it, to do something useful. You can write a method_missing method which will catch any unknown method calls, and decide what to do with them.

This example simply lists the parameters - the first is the invoked method name:
def method_missing *args
puts "method_missing:"
args.each { x
puts x
}
end

Or this version explicitly pulls out the method name in the parameter list:
def method_missing method_id, *args
puts "method_missing: " + method_id.to_s
args.each { x
puts x
}
end

Rails uses this to handle the find_by_[column] method calls, for instance, checking if the method call starts find_by_ and ends with a column name, throwing an exception if not.

Note that method_missing is entirely different to self.method_missing. The former handles unknown instance method calls, the latter for unknown class method calls.

You can override method_missing in a superclass in your own class. If the method call fails to match what you were expecting, you can pass the responsibility on to the superclass, using the super keyword.
def method_missing *args
if matches args[0].to_s
do_stuff
else
super
end
end

super can cope with def method_missing *args or def method_missing method_id, *args

Trying to invoke an unknown method in method_missing will cause a stack overflow!

This web page makes a good case for moving method_missing to a new class dedicated to dynamically handling method calls:
http://blog.jayfields.com/2008/03/ruby-isolate-dynamic-receptor.html

Struggling with Ruby: Contents Page

No comments: