Understanding method_missing in Ruby
Overview
In this post we will review how to use the method_missing
method in Ruby together with some guidelines and best practices.
Do not use method_missing as the first option of solving a particular problem
First of all, it is not advised to overwrite the private method BasicObject#method_missing
as the first option of solving a particular problem.
Like all metaprogramming solutions, use this technique only if it saves time or if you failed to come up with a simpler way to solve the problem.
Simple is better than complex. This is one of the main reasons this technique is not frequently applied in practice. If not properly implemented, we may end up in an infinite recursion.
So, what is method_missing used for?
It gives us one last possibility to cope with the missing method prior to raising the well known NoMethodError
exception.
When a given object obj
receives a nonexisting method call (message) (e.g. obj.hello
), the interpreter starts crawling the ancestors chain in an attempt to find a suitable method to invoke. If Ruby fails to find a suitable method it raises NoMethodError
.
We can modify this behaviour by overwriting BasicObject#method_missing
somewhere in the ancestors chain and handle the missing method definitions in a custom way.
Introspection and method_missing
The intentions of using this technique is to treat (some of) the missing methods as ones that are already defined (e.g. having a certain method name pattern or some other rule).
Therefore, we need to also overwrite the BasicObject#respond_to_missing?
method so introspection (e.g. with respond_to?
, method
, etc.) works as expected.
Example using method_missing
DISCLAIMER: This is a code-smell and one should consider using a simpler approach to such solutions.
Imagine that you want to have your Foo
class to handle all methods starting with check_
in such a way that a check to an external service is made.
Here is a simple example implementation of the Foo
class:
...and now some method invocations:
Conclusion
- Overwrite
BasicObject#method_missing
only if you failed to come up with a simpler solution. - Always overwrite
BasicObject#respond_to_missing?
when definingmethod_missing
method. - One should be extremely cautious when using this technique as there is a risk to end up in an infinite recursion.
- Consider using
super
in the definitions of the two methods.