Leigh Halliday
YouTubeTwitterGitHub

Understanding method missing

published Sep 8, 2015

In this article we are going to look at method_missing and respond_to_missing? in order to see what they do and how they can be used. We're going to re-create the StringInquirer class in Ruby on Rails as a way to demonstrate what is happening and how it can be used in real applications.

What is StringInquirer

If you've used Rails you've probably had to check at one point or another which environment your app is running in. To do this you probably called Rails.env.staging?. What I didn't know until recently was that env was an instance of the StringInquirer class.

StringInquirer allows you to check if a String is equal to something by asking it staging? rather than env == "staging".

NoMethodError

You've probably seen the NoMethodError exception many times while programming Ruby. The code below will produce the NoMethodError exception because the method happy? doesn't exist.

class StringInquirer < String
end

mood = StringInquirer.new("happy")
mood.happy?

When you call happy? Ruby will check to see if your object "responds to" this method... or in other words, if a method exists with that name either in the current object, or any other class up the hierarchy chain.

In this case it can't find the method happy? so it raises the exception; but Ruby allows us to implement a method called method_missing which is called as a last ditch attempt to find a receiver for this method call. method_missing receives the name of the method that was being called along with the rest of the arguments that were passed to the method we wanted to call.

If we add method_missing to our class and print out the values passed to it, we can get an idea of what we're working with.

class StringInquirer < String

private
def method_missing(method_name, *arguments)
puts method_name.inspect
puts arguments.inspect
end
end

Which will print out the following:

:happy?
[]

Using this knowledge, we want to check if the method_name ends with a question mark and, if so, check to see if the beginning of the method name is equal to the value of our string. If it doesn't end with a question mark, we can call super passing off responsibility to some parent class.

class StringInquirer < String

private
def method_missing(method_name, *arguments)
if method_name[-1] == '?'
self == method_name[0..-2]
else
super
end
end
end

This updated code now correctly returns true when asking happy? and will return false if we asked it something like sad?.

Does my object respond to?

Sometimes before you call a method you want to first ask your object if it will be able to respond to this method. To do this you can use the method respond_to? passing the method name to the method.

"Happy".respond_to?(:length) # => true

But because our question mark methods happy? or sad? don't actually exist, it's going to give us false no matter what.

There is one other method we can implement to help us handle the case where it can't find a concrete method to call. This is respond_to_missing?.

respond_to_missing? receives the name of the method and another value called include_private which we won't worry about now. In our case, we basically want to check if the method being called ends with a question mark or not.

This leads us to our final implementation of the StringInquirer class, which has implemented both method_missing and respond_to_missing?.

class StringInquirer < String
private

def respond_to_missing?(method_name, include_private = false)
method_name[-1] == '?'
end

def method_missing(method_name, *arguments)
if method_name[-1] == '?'
self == method_name[0..-2]
else
super
end
end
end

We can use the class like so:

mood = StringInquirer.new("happy")
p mood.happy? # => true
p mood.sad? # => false
p mood.respond_to?(:happy?) # => true

Concluding thoughts

We've covered the use of 2 methods (method_missing and respond_to_missing?) to perform a little bit of what is called "Meta Programming". Allowing our code to ask questions about itself and create new functionality on the fly.

When you want to add a little "syntactic sugar" to your code, perhaps using these 2 methods will help you achieve that. Worst case scenario is that you've come away with knowledge of how the StringInquirer class - which is part of the ActiveSupport module of Rails - works. The next time you use Rails.env.development? you'll know what is happening. I recommend Metaprogramming Ruby 2 to go deeper on this subject.