O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.
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()
Próximos SlideShares
Carregando em…5
×

The Revenge of method_missing()

4.056 visualizações

Publicada em

Slides of my presentation at EuRuKo 2011 in Berlin.

Publicada em: Tecnologia, Negócios
  • 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.
       Responder 
    Tem certeza que deseja  Sim  Não
    Insira sua mensagem aqui
  • 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 :)
       Responder 
    Tem certeza que deseja  Sim  Não
    Insira sua mensagem aqui
  • 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.
       Responder 
    Tem certeza que deseja  Sim  Não
    Insira sua mensagem aqui
  • https://gist.github.com/997005 is a way to make it work
       Responder 
    Tem certeza que deseja  Sim  Não
    Insira sua mensagem aqui

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.

×