Let’s get meta: missing method
If you are coming from PHP, or C# you might not find yourself thinking in terms of meta programming – although both languages can do meta programming, it doesn’t seem to be heavily used. This isn’t the case in Ruby – quite the opposite – meta programming is a HUGE part of Ruby.
So what is meta programming? It is basically a way of modifying programs dynamically, like adding or changing methods AFTER the program has started running (or at runtime as we like to call it).
A great example of this is the find_by_* methods in rails. If you have a model called “User” that has a column called “age” you can search your records by age using the magic method User.find_by_age. This is accomplished using the method_missing method.
If you call a method that doesn’t exist, Ruby will call the method_missing method, and pass the name of the method and any arguments you supplied, which means you can dynamically handle the method.
class MyClass
def find(name, value)
puts "You want results from #{name} with a value of #{value}"
end
def method_missing(id, *args)
return self.find(Regexp.last_match(1), args[0]) if id.id2name =~ /find_by_(.+)/
raise NoMethodError
end
end
m = MyClass.new
m.find('name', 'madpilot')
> You want results from name with a value of madpilot
m.find_by_name('madpilot')
> You want results from name with a value of madpilot
m.search_by_name('madpilot')
> NoMethodError: NoMethodError
As you can see, there is definitely no “find_by_name” method, yet Ruby is smart enough to call the find method with the correct parameters. What we have done is to run a regular expression against the name of the method, and if it matches the pattern find_by_[string] then it calls the find method with the [string] part as the first parameter and the first argument as the second parameter. Pretty powerful, don’t you think?
Just a tip though – be careful of endless loops – if your method_missing method doesn’t terminate and you call a method that doesn’t exists inside method_missing, you’ll be in all sorts of trouble!