Implementing DRY Magic Methods in Ruby
As a new developer to Ruby you might wonder how certain methods seem to be magically available without being strictly defined. Rails’s dynamic finders (e.g.
find_by_name) are one example of this kind of magic. It’s very simple to implement magic such as this in Ruby, but it’s also easy to implement things in a way that doesn’t entirely mesh with standard Ruby object expectations.
The way that many magic methods are implemented is by overriding
method_missing. This special method in Ruby is automatically called by the interpreter whenever a method is called that cannot be found. The default behavior of
method_missing is to raise a
NoMethodError letting the user know that the method that was called does not exist. However, by overriding this behavior we can allow the user to call methods that aren’t strictly defined but rather programatically determined at runtime. Let’s look at a simple example:
class Nullifier def method_missing(*args) nil end end nullifier = Nullifier.new nullifier.some_method # => nil nullifier.foo(:bar, :baz) # => nil
Here we simply told
method_missing to immediately return nil, regardless of the method name or arguments passed. This essentially means that, for this class, any method call that is not defined on
Object (the default superclass for new classes) will return nil.
While this example is certainly interesting, it doesn’t necessarily give us more use in the real world. Let’s take another example that actually does something useful. Let’s make a hash that allows us to access its keys by making method calls:
class SuperHash < Hash def method_missing(method_name, *args) if key?(method_name.to_s) self[method_name].to_s else super end end end h = SuperHash.new h['abc'] = 'def' h.abc # => 'def' h.something_else # => NoMethodError
This behavior gives us something pretty simple yet powerful: we have manipulated the foundation of the class to give us runtime methods. There’s a problem, though: using
method_missing alone is only half the story.
Quack Check With
In Ruby, you can call
respond_to? with a symbol method name on any object and it should tell you whether or not that method exists on the object in question. This is part of what makes Ruby’s duck-typing work so well. So in our example, we also want to be able to know if a method is there using
respond_to?. So let’s add a new override for the
respond_to? method of our example above:
class SuperHash < Hash def respond_to?(symbol, include_private=false) return true if key?(symbol.to_s) super end end
Well, that was easy enough. Now our SuperHash will return hash keys based on
method_missing and even tell you if the method is there with
respond_to?. But there’s still one more thing we can do to clean things up a bit: notice how we have repeated functionality in that we check
key? in both methods? Now that we have a
respond_to? we can use that as a guard for
method_missing to make it more confident:
class SuperHash < Hash def method_missing(method_name, *args) return super unless respond_to?(method_name) self[method_name].to_s end end
Wait, that can’t be right, can it? Can we just assume that we can call the key like that? Of course! We already know that no existing method was called if
method_missing is activated. That means that if
respond_to? is true but no existing method was called, there must be a key in our hash that caused
respond_to? to return true. Therefore we can confidently assume that the key exists and simply return it, removing the conditional and cleaning up the
Now that you know how
respond_to? can work together to add functionality to an object at runtime, you have a powerful new tool in your metaprogramming arsenal. Enjoy it!