Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
The revenge of method_missing()       Paolo “Nusco” Perrotta
Should I use method_missing()  to remove duplication frommy code, or should I avoid it       like the plague?      Paolo “...
disclaimer
Person.find_by_user_name_and_password name, pwd
Person.find :user_name => name, :password => pwd
class NilClass  # ...  def method_missing(method, *args, &block)    if klass = METHOD_CLASS_MAP[method]      raise_nil_war...
end of disclaimer
class InfoDesk  def flights    # ...  end  def trains    # ...  end  def hotels    # ...  end  # ...end
class Sign  def initialize    @desk = InfoDesk.new  end  def flights    return “Out for lunch” if Clock.lunch_time?    @de...
class Sign  def initialize    @desk = InfoDesk.new  end  def method_missing(name, *args)    return “Out for lunch” if Cloc...
Ghost Methods
class Sign  def initialize    @desk = InfoDesk.new  end  InfoDesk.public_instance_methods.each do |m|    define_method(m) ...
Ghost Methods      vs.Dynamic Methods
the four pitfallsof method_missing()
class Sign  def initialize    @desk = InfoDesk.new  end  def method_missing(name, *args)    return “Out for lunch” if Cloc...
we have a problem
class Sign  def initialize    @desk = InfoDesk.new  end  def is_it_lunch_time_yet?    Clock.lunch_time?  end  def method_m...
class Sign  def initialize    @desk = InfoDesk.new  end  def is_it_lunch_time_yet?    Clock.lunch_time?  end  def method_m...
class Sign  def initialize    @desk = InfoDesk.new  end  def is_it_lunch_time_yet?    Clock.lunch_time?  end  def method_m...
the first pitfall: the Ghost House
class Sign  def initialize    @desk = InfoDesk.new  end  def method_missing(name, *args)    super unless @desk.respond_to?...
we have a problem
Sign.new.respond_to? :flights   # => false
the second pitfall:  the Liar Object
class Sign  def initialize    @desk = ::InfoDesk.new  end  def method_missing(name, *args)    super unless @desk.respond_t...
we have a problem
class InfoDesk  def flights    # ...  end  def local_transports    # ...  end  # ...  def display    # ...  endend
Sign.new.display   # => #<Sign:0x000001008451b0>
the third pitfall:  the Fake Ghost
class Sign  instance_methods.each do |m|    undef_method m unless m.to_s =~ /^__|object_id/  end  def initialize    @desk ...
class Sign < BasicObject  def initialize    @desk = ::InfoDesk.new  end  def method_missing(name, *args)    super unless @...
we have a problem    (this is getting old already)
the fourth pitfall:  the Snail Ghost
class Sign < BasicObject  def initialize    @desk = ::InfoDesk.new  end  def method_missing(name, *args)    super unless @...
class Sign < BasicObject  def initialize    @desk = ::InfoDesk.new  end  def method_missing(name, *args)    super unless @...
class Sign < BasicObject  def initialize    @desk = ::InfoDesk.new  end  def method_missing(name, *args)    super unless @...
should I use method_missing()   to remove duplication in           my code?
not if you can usedefine_method() instead
just a rule of thumb
remember the four pitfalls:         the Ghost House         the Liar Object         the Fake Ghost         the Snail Ghost
thank you.
The Revenge of method_missing()
The Revenge of method_missing()
The Revenge of method_missing()
The Revenge of method_missing()
The Revenge of method_missing()
The Revenge of method_missing()
The Revenge of method_missing()
The Revenge of method_missing()
Upcoming SlideShare
Loading in …5
×

The Revenge of method_missing()

3,998 views

Published on

Slides of my presentation at EuRuKo 2011 in Berlin.

Published in: Technology, Business
  • That's totally reasonable, and I had similar feedback from Matz himself - so next time I deliver the speech, I'll make it clear that you can make it work. :)
    My point is that getting it right in the first place is enough work that the alternative (define_method()) turns out to be easier in general.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Yep the presentation was very good on these pitfalls (and funny), it's just that I don't like to see code not working when it could work :)
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Agreed (Matz also called my attention to this). My point is not that you cannot make it work - you can, but the solution is complicated enough, and the pitfalls along the way slippery enough, that my final decision is not to use method_missing() in most cases where I can use define_method() instead.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • https://gist.github.com/997005 is a way to make it work
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

The Revenge of method_missing()

  1. 1. The revenge of method_missing() Paolo “Nusco” Perrotta
  2. 2. Should I use method_missing() to remove duplication frommy code, or should I avoid it like the plague? Paolo “Nusco” Perrotta
  3. 3. disclaimer
  4. 4. Person.find_by_user_name_and_password name, pwd
  5. 5. Person.find :user_name => name, :password => pwd
  6. 6. class NilClass # ... def method_missing(method, *args, &block) if klass = METHOD_CLASS_MAP[method] raise_nil_warning_for klass, method, caller else super end endend
  7. 7. end of disclaimer
  8. 8. class InfoDesk def flights # ... end def trains # ... end def hotels # ... end # ...end
  9. 9. class Sign def initialize @desk = InfoDesk.new end def flights return “Out for lunch” if Clock.lunch_time? @desk.flights end def trains return “Out for lunch” if Clock.lunch_time? @desk.local_transports end def hotels return “Out for lunch” if Clock.lunch_time? @desk.local_transports end # ...end
  10. 10. class Sign def initialize @desk = InfoDesk.new end def method_missing(name, *args) return “Out for lunch” if Clock.lunch_time? @desk.send(name, *args) endend# At 12:30...Sign.new.flights # => “Out for lunch”
  11. 11. Ghost Methods
  12. 12. class Sign def initialize @desk = InfoDesk.new end InfoDesk.public_instance_methods.each do |m| define_method(m) do return “Out for lunch” if Clock.lunch_time? @desk.send(m) end endend
  13. 13. Ghost Methods vs.Dynamic Methods
  14. 14. the four pitfallsof method_missing()
  15. 15. class Sign def initialize @desk = InfoDesk.new end def method_missing(name, *args) return “Out for lunch” if Clock.lunch_time? @desk.send(name, *args) endend
  16. 16. we have a problem
  17. 17. class Sign def initialize @desk = InfoDesk.new end def is_it_lunch_time_yet? Clock.lunch_time? end def method_missing(name, *args) return “Out for lunch” if is_it_lunchtime_yet? @desk.send(name, *args) endend# At 12:30...Sign.new.flights # => ?
  18. 18. class Sign def initialize @desk = InfoDesk.new end def is_it_lunch_time_yet? Clock.lunch_time? end def method_missing(name, *args) return “Out for lunch” if is_it_lunchtime_yet? @desk.send(name, *args) endend# At 12:30...Sign.new.flights # => ?
  19. 19. class Sign def initialize @desk = InfoDesk.new end def is_it_lunch_time_yet? Clock.lunch_time? end def method_missing(name, *args) return “Out for lunch” if is_it_lunchtime_yet? @desk.send(name, *args) endend# At 12:30...Sign.new.flights # => SystemStackError: stack level too deep
  20. 20. the first pitfall: the Ghost House
  21. 21. class Sign def initialize @desk = InfoDesk.new end def method_missing(name, *args) super unless @desk.respond_to? name return “Out for lunch” if Clock.lunch_time? @desk.send(name, *args) endend
  22. 22. we have a problem
  23. 23. Sign.new.respond_to? :flights # => false
  24. 24. the second pitfall: the Liar Object
  25. 25. class Sign def initialize @desk = ::InfoDesk.new end def method_missing(name, *args) super unless @desk.respond_to? name return “Out for lunch” if Clock.lunch_time? @desk.send(name, *args) end def respond_to?(method) @desk.respond_to?(method) || super endendSign.new.respond_to? :flights # => true
  26. 26. we have a problem
  27. 27. class InfoDesk def flights # ... end def local_transports # ... end # ... def display # ... endend
  28. 28. Sign.new.display # => #<Sign:0x000001008451b0>
  29. 29. the third pitfall: the Fake Ghost
  30. 30. class Sign instance_methods.each do |m| undef_method m unless m.to_s =~ /^__|object_id/ end def initialize @desk = InfoDesk.new end def method_missing(name, *args) super unless @desk.respond_to? name return "Out for lunch" if Clock.lunch_time? @desk.send(name, *args) end def respond_to?(method) @desk.respond_to? method || super endendSign.new.display # => InfoDesk#display() called
  31. 31. class Sign < BasicObject def initialize @desk = ::InfoDesk.new end def method_missing(name, *args) super unless @desk.respond_to? name return “Out for lunch” if ::Clock.lunch_time? @desk.send(name, *args) end def respond_to?(method) @desk.respond_to? method || super endendSign.new.display # => InfoDesk#display() called
  32. 32. we have a problem (this is getting old already)
  33. 33. the fourth pitfall: the Snail Ghost
  34. 34. class Sign < BasicObject def initialize @desk = ::InfoDesk.new end def method_missing(name, *args) super unless @desk.respond_to? name singleton_class.send :define_method, name do return “Out for lunch” if ::Clock.lunch_time? @desk.send name end send name end def respond_to?(method) @desk.respond_to? method || super endend# At 12:30...Sign.new.flights # => ?
  35. 35. class Sign < BasicObject def initialize @desk = ::InfoDesk.new end def method_missing(name, *args) super unless @desk.respond_to? name singleton_class.send :define_method, name do return “Out for lunch” if ::Clock.lunch_time? @desk.send name end send name end def respond_to?(method) @desk.respond_to? method || super endend# At 12:30...Sign.new.flights # => SystemStackError: stack level too deep
  36. 36. class Sign < BasicObject def initialize @desk = ::InfoDesk.new end def method_missing(name, *args) super unless @desk.respond_to? name singleton_class.send :define_method, name do return “Out for lunch” if ::Clock.lunch_time? @desk.send name end send name end def respond_to?(method) @desk.respond_to? method || super endend# At 12:30...Sign.new.flights # => SystemStackError: stack level too deep
  37. 37. should I use method_missing() to remove duplication in my code?
  38. 38. not if you can usedefine_method() instead
  39. 39. just a rule of thumb
  40. 40. remember the four pitfalls: the Ghost House the Liar Object the Fake Ghost the Snail Ghost
  41. 41. thank you.

×