Ruby’s dynamism is awe-inspiring. One of the methods that contribute to this is method_missing. method_missing is defined in BasicObject. method_missing allows us to:

Let’s discuss the practical uses of this method.

What is Ruby’s method_missing?

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. 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):

Ruby method_missing lookup path animated.

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 BasicObject, method_missing is called. The interpreter raises an error at this point.

Here’s an example:

class Aircraft
  attr_accessor :passengers

  def initialize
    @passengers = 0
  end

  def taxi
  end

  def park
  end
end

aircraft = Aircraft.new
aircraft.fly

Here’s what happens when you call an inexistent method on an object: When the search for a #fly method in the inheritance tree ends in vain, then the method call aircraft.fly will output <main>: undefined method 'fly' for #<Aircraft:0x00007fe66c1591e0 @passengers=0> (NoMethodError).

It would be nice to be able to handle this error in a friendlier way. Ruby permits this by making programmers override method_missing. 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 Aircraft code:

class Aircraft
  attr_accessor :passengers

  def initialize
    @passengers = 0
  end

  def method_missing(method_name, *args)
    message = "You called #{method_name} with #{args}. This method doesn't exist."

    raise NoMethodError, message
  end
end

aircraft = Aircraft.new
aircraft.add_passengers(275)

With this change, we now have 'method_missing': You called add_passengers with [275]. This method doesn't exist. (NoMethodError) 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:

class Roman
  def roman_to_int(str)
    # ...
  end

  def method_missing(symbol, *args)
    str = symbol.id2name
    begin
      roman_to_int(str)
    rescue
      super(symbol, *args)
    end
  end
end

r = Roman.new

r.iv      #=> 4
r.xxiii   #=> 23
r.mm      #=> 2000
r.foo     #=> NoMethodError

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 SystemStackError.

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_”.

class Aircraft
  attr_accessor :passengers

  def initialize
    @passengers = 0
  end

  def method_missing(method_name, *args, &block)
    if method_name =~ /add_(.*)/
      public_send("#{Regexp.last_match(1)}=", *args)
    else
      super
    end
  end
end

aircraft = Aircraft.new
aircraft.add_passengers(275)

p aircraft.passengers #=> 275
p aircraft.respond_to?(:add_passengers) #=> false

In the 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 method_missing.

In our case, we add:

def respond_to_missing?(method_name, include_private = false)
  method_name =~ /add_(.*)/ || super
end
class Aircraft
  attr_accessor :passengers

  def initialize
    @passengers = 0
  end

  def method_missing(method_name, *args, &block)
    if method_name =~ /add_(.*)/
      public_send("#{Regexp.last_match(1)}=", *args)
    else
      super
    end
  end

  def respond_to_missing?(method_name, include_private = false)
    method_name =~ /add_(.*)/ || super
  end
end

aircraft = Aircraft.new
aircraft.add_passengers(275)

p aircraft.passengers #=> 275
p aircraft.respond_to?(:add_passengers) #=> true

Method Delegation With method_missing

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:

class Referee
  def red_card?
    puts "true"
  end
end


class Linesman
  def initialize(referee)
    @referee = referee
  end

  def method_missing(method, *args)
    @referee.send(method)
  end

end

linesman = Linesman.new(Referee.new)

linesman.red_card? # => true

In the code above we call red_card? on 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 method_missing

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:

  • Capistrano
  • Rake
  • Sinatra
  • Rails Routing
  • factory_bot

Ruby’s method_missing makes it very easy to build DSLs. Let’s try our shot with the simplest XML generator. Here’s a modified snippet I grabbed from The Ruby Programming Language book.

class XML
  class XML
  def initialize(output)
    @output = output
  end

  def method_missing(tag, attributes = {})
    @output << "<#{tag}"

    if block_given?
      @output << '>'
      content = yield

      @output << content.to_s if content

      @output << "</#{tag}>"
    else
      @output << '/>'
    end
    nil
  end

  def self.generate(output, &block)
    XML.new(output).instance_eval(&block)
  end
end

This code allows us to write something like:

  XML.generate(STDOUT) do
  html do
    head do
      title "Page Title"
    end
    body do
     strong do
      "Strong Text"
     end
    end
  end
end

Running this produces:

<html><head><title/></head><body><strong>Strong Text</strong></body></html>

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 method_missing

I learned yesterday from a group that Ruby does the traversal for method_missing twice. The first one is to reach the method_missing in 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.

Summary

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.

    Powered By ConvertKit
    Follow me on Twitter to explore Ruby, JavaScript and web technologies. Consider subscribing to my newsletter or buying me a coffee if you found any of my content helpful.