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!

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Neil Mosafi

    This sounds like it should be powerful, but I’m struggling to understand why! Why would I not just call m.find(‘name’, ‘madpilot’) like you did in the first example? How does having a missing method help me as a user of your API?

  • http://www.deanclatworthy.com Dean C

    Just a heads up to those reading the comments. You can do the same thing using PHP’s __call method within any class. http://uk2.php.net/__call

  • madpilot

    Neil: The example is a little contrived, but it does provide convenience. But an example of where it becomes powerful in Rails is the has_many and belongs_to associations.

    If you add has_many :employees to a Company model, Rails knows to respond to company.employees and company.employees=

    Another example is the ruby Flickr API – it uses method_missing to convert the API endpoints to Ruby methods.