When Using method_missing, Update respond_to

Because Ruby is dynamically typed, respond_to? is an important method. Although sometimes is_a? will creep into code, respond_to? should always be considered first.

# Instead of
def my_method(input)
  input.quack if input.is_a?(Duck)

end

# We want
def my_method(input)
  input.quack if input.respond_to?(:quack)
end

We don't care if it's a duck, we only care if it quacks.

A problem arises when developers start to use method_missing to implement methods. Let's say a developer did something like this:
class Duck
  def method_missing(method, *args, &block)
    if method.to_s =~ /^quack/
      puts "quack!"
    else
      super
    end
  end
end

For arbitrary rationale, this would allow the duck to quack any of the following ways:
duck.quack
duck.quack!
duck.quack_loudly

However, we've created a problem with respond_to?.
duck.respond_to?(:quack) #=> false
duck.quack #=> 'quack!'

We have now limited our ability to use dynamic typing. We cannot depend on respond_to? telling us if our object can quack. Therefore, when using method_missing, remember to update respond_to?
class Duck
  QUACK_METHOD = /^quack/

  def respond_to?(method)
    return true if method =~ QUACK_METHOD
    super
  end

  def method_missing(method, *args, &block)
    if method.to_s =~ QUACK_METHOD
      puts "quack!"
    else
      super
    end
  end
end

Although method_missing can still be problematic, this makes it a little less painful.
Hi, I'm Dan Manges, a software developer best known for being the founding CTO of Braintree. Send me an email.
blog comments powered by Disqus