3 Practical Uses of Ruby's `method_missing` Worth Knowing.• ruby, metaprogramming • siaw23
Ruby’s dynamism is awe-inspiring. One of the methods that contribute to this is method_missing.
method_missing is defined in
method_missing allows us to:
Let’s discuss the practical uses of this method.
What is Ruby’s
We define methods inside classes. Ruby traverses a method lookup path when an object calls a method, starting from the object’s class and up the object’s class’s ancestor chain to reach the method. If the method the object calls is available in the lookup path, Ruby calls it. On some occasions, the method isn’t present in the ancestor chain.
All method lookups that fail end up reaching a method called
method_missing lives inside the BasicObject class, the root of all classes.
For a class,
Aircraft, that mixes in no other modules or prepends other module or inherit other classes, this is how the traversal would look like (I’ve omitted the lookup through eigenclasses for simplicity):
method_missing For Error Handling
Perhaps the most common use of
method_missing is its support for gracefully handling errors. When an object invokes a method, Ruby navigates the method lookup chain looking for the method to execute. On reaching
method_missing is called. The interpreter raises an error at this point.
Here’s an example:
Here’s what happens when you call an inexistent method on an object: When the search for a
#drift method in the inheritance tree ends in vain, then the method call
aircraft.drift will output:
It would be nice to be able to handle this error in a friendlier way. Ruby permits this by making programmers override
method_missing takes three parameters; a method name, its list of arguments and a block. The last two are optional.
To handle these errors, you intercept the call to BasicObject’s method_missing with your own. Here’s a rewrite of our
With this change, we now have as output:
This looks more welcoming than the error we saw before. This example shows the extent to which Ruby allows programmers to own their code. A slightly more sophisticated example may involve the use of
super where if someone calls an inexistent method, you’d want first to perform some operation on the method and then only pass control to the original
method_missing method as shown in this example from Ruby’s documentation:
With more complicated usages of
method_missing, come more significant responsibilities. When you override
method_missing, you have to make sure that whatever method you call inside it is available. Otherwise, you’ll end up going in circles and eventually get a
Expanding on the
Aircraft class, here’s something more meaningful. If a method call on an
Aircraft instance starts with “add”, we call
public_send on the instance passing it a setter method that sets the variable of whatever comes after “add”.
method_missing method, if the method called on any instance of
Aircraft matches “add_”, we call a setter method and pass it the arguments that came with the method call.
However, it would help if you took note that
respond_to “add_passengers” returns false, this is not true, and that’s why it’s good practice always to override
respond_to_missing? method anytime you override
In our case, we add:
Method Delegation With
According to Wikipedia, delegation refers to evaluating a member (property or method) of one object (the receiver) in the context of another original object (the sender), this means, an object is asking another object to perform the actions in the method the receiver is calling.
You can use Ruby’s method_missing to delegate methods to another class.
Here’s a contrived example:
In the code above we call
linesman, an instance of
Linesman, but it’s an instance of
Referee that does the job, this is an example of some form of delegation.
Ruby provides a library called delegate in its Standard Library that you might want to check out first if you need to do serious method delegation,
Building DSLs And Libraries With
A domain-specific language (DSL) is a language with which you can do specific tasks. Unlike Ruby, a DSL like RSpec only useful for testing.
DSLs are relatively easier to write. DSLs are everywhere. A few examples are:
- Rails Routing
Ruby’s method_missing makes building DSLs accessible. Let’s try our shot with the simplest XML generator. Here’s a modified snippet I grabbed from The Ruby Programming Language book.
This code allows us to write something like:
Running this produces:
And just like that, we’ve harnessed the power of
method_missing to create a DSL that generates XML.
An excellent example of a DSL that relies on
method_missing is The Late Jim Weirich’s Builder gem. The Builder gem generates XML markup from blocks, much like what we just wrote but more sophisticated.
Another example that might seem familiar is Ruby on Rails’s dependence on
method_missing to create dynamic finders like the
find_by_* group of methods in ActiveRecord.
Question On The Traversal For
I learned yesterday from a group that Ruby does the traversal for
method_missing twice. The first one is to reach the
BasicObject, and the second traversal is to check if you overrode
method_missing: this makes sense, otherwise, how would Ruby pick up the
method_missing you’ve overwritten.
I haven’t found any reference on this. Please share if you have some documentation somewhere about this fact.
We’ve seen how to use
method_missing to handle errors and to do method delegation. With the help of another metaprogramming construct, we saw how to use
instance_eval to write a DSL that generates XML. We looked at how Ruby traverses the ancestor chain to find methods and calls
method_missing if it finds nothing. We learned that it’s a good idea to overwrite
respond_to_missing? any time we overwrite
method_missing?; this covers the list of things you can do with
method_missing. If you can think of any other use cases, please share them in the comments below.