How to use method_missing
There’s no doubt, Ruby can be tricky at times. But understanding it well (and not that I do) can greatly increase productivity, efficiency, and happiness. Seriously.
Would you believe me if I said I could randomly call Model.ryan_heath and dynamically teach Ruby that ryan_heath is a real method under the Model class? No, Ruby doesn’t come pre-packaged with a method that just-so-happens to be my name. There’d be no trick in that. Rather, this works because of method_missing. If you’ve never heard of method_missing, it’s simply a method that gets invoked after Ruby searches the entire class hierarchy for a method that doesn’t exist (Model.ryan_heath would result in Object.method_missing). So what, right? Wrong. Since methods can be easily overridden in Ruby, you can use this little hook to really help you write (or not write) code.
As an example (albeit, a shallow one) to show how flat out awesome this technique is, I’m going to use a snippet from Golf Trac (which is taking me forever to finish, by the way). So, in my golf application I have the obvious association that “a course has many holes”. Since I’ll be referencing those holes a good bit throughout the application, it would be nice to have a convenient way to access them. I’ll rarely (if at all) be asking for hole information outside the context of a course, and so it’d be best to define a method directly on the course-to-holes association.
While this can simply be any method I’d like, I chose to override method_missing (you’ll see my original intention in a minute). Here’s the top of my Course model:
1 2 3 4 5 6 7 8 9 10 11 12 | class Course < ActiveRecord::Base has_many :rounds, :include => :scores has_many :holes do def method_missing(method) find( :first, :conditions => { :hole_number => method.to_s.send(:gsub, /[a-z|A-Z|_]/, "").to_i } ) end end ... end |
Here, find will always be scoped to a specific course, so I only need to worry about getting the appropriate hole. Now, to access the holes:
1 2 3 4 | @course.holes.hole_10 # => tenth hole @course.holes.hole_18 # => eighteenth hole |
The best part is, the hole_10 and hole_18 methods don’t really exist! How cool is that?
Also, looping through all of the holes using the above implementation may seem to make sense, but it doesn’t. If I were to use the above jargon, I’d be doing a separate query for each hole in the loop. In that case, I’d probably rather eager load the holes instead. However, if I didn’t care to run 18 separate queries, it could be done like so:
1 2 3 4 5 6 7 8 | (1..18).each do |i| puts "Par for the #{i.ordinalize} hole is #{@course.holes.send("hole_#{i}".to_sym).par}." end ## results # => "Par for the 1st hole is 4." # => "Par for the 2nd hole is 3." # => ... |
I’ve essentially told Ruby that there are 18 methods (hole_1, hole_2, ...hole_18) defined on the course-to-holes association, when really there’s nothing more than a single method_missing hook. That’s just awesome to me.
And now it’s time for my original intention. Originally, I had planned on using the hole number itself as the method, which would have been easier and much cleaner. But I can’t seem to get it to work. I’m currently using course.holes._10 instead of the more redundant, course.holes.hole_10, but what I really want is just course.holes.10.
1 2 3 4 5 6 7 8 | def method_missing(method) find(:first, :conditions => { :hole_number => method.to_i }) end @course.holes.18 # => eighteenth hole @course.holes.11 # => eleventh hole |
But, and correct me if I’m wrong, it’s apparent that Ruby doesn’t quite understand integers as method calls. Oh well, Ruby does so many things that amaze me, it was worth a shot. If anyone has a thought or two as to how I can get course.holes.18 to return the 18th hole, I’d love to hear them.
I still have a lot, a lot, to learn about metaprogramming, but it definitely feels good to have your code do extra work for you. If you have a clever method_missing example, feel free to leave it in the comments.

Chris Sunday, 05 Aug, 2007 Posted at 06:56PM
How about
@course.holes[18]?Ryan Sunday, 05 Aug, 2007 Posted at 07:12PM
Funny, I just came across Jamis Buck’s finder shortcut (again).
That’s perfect, though—thanks.
Rutger Tuesday, 17 Nov, 2009 Posted at 09:40AM
Being a Rails newbie, i’m seeking for a solution to custom (database) fields. If i came up with something clever, i’ll let you know. (But i doubt it, since it seems i’m in over my head as a newbie :))