Mittwoch, 29. Mai 2013

Respond to method missing

You have to deal with dynamic method calls and somebody threw an "Implement method_missing" at you?
You google how to do it and maybe find an article like "method_missing, baby!". You code what you gotta code...
But that's only half of the truth. You probably missed to "respond_to?":
class Person
private
  def method_missing method_name, *args
    if %w(runs walks goes drives).include?(method_name.to_s)
      puts "#{self.class} #{method_name}."
    else
      super
    end
  end
end
which means, you broke the POLS (Principle Of Least Surprise):
person = Person.new
person.respond_to? :runs # => false
person.runs # => "Person runs."
Curious! An object, which claims not to respond to a receiver, but instead seem to respond? None would expect such behavior. The solution is to overwrite Person#respond_to? likewise:
class Person
  MOTIONS = %w(runs walks goes drives)
  def respond_to? method_name
    MOTIONS.include? method_name.to_s
  end

private
  def method_missing method_name, *args
    if MOTIONS.include?(method_name.to_s)
      puts "#{self.class} #{method_name}."
    else
      super
    end
  end
 end
and accordingly:
person = Person.new
person.respond_to? :runs # => true
person.runs # => "Person runs."
If it walks like a duck and if it quacks like duck, it is a duck. Ruby duck typing.
Lastly I want to point to another great article about the Principle Of Least Surprise: The Tao Of Programming. Funny.

Supported by Ruby 1.9.3

3 Kommentare:

  1. There was a discussion of this on Avdi Grimm's blog about a year ago. See:

    http://devblog.avdi.org/2011/12/07/defining-method_missing-and-respond_to-at-the-same-time/

    including some code from me to fix the problem, kinda-sorta. Maybe I should get back to packaging that up as a gem, as I intended....

    BTW, any chance of getting some internationalization on your blog buttons? I had to go over to Google Translate to figure out what Veroffentlichen and Vorschau mean.

    AntwortenLöschen
    Antworten
    1. Since way back when, I've done more work on this. Check out my "Hook Lying Syncer" gem, at https://github.com/davearonson/hook_lying_syncer . (For those of you who don't get the joke, there's an expression in English, to "swallow" a lie or fall for a scam completely is to do so "hook, line, and sinker", referring to a fishing hook, the fishing line it's on, and the small lead weight that ensures the hook is down in the water.)

      Löschen
  2. Great blog post. Thank you.
    Do you work with Avdi Grimm?
    I love the Ruby Rogues podcasts (http://rubyrogues.com), which he is involved. They are awesome.

    AntwortenLöschen