meer_method_misbruik

Geplaatst door Michiel de Mare ma, 12 jun 2006 12:12:00 GMT

Terwijl ik aan het nadenken was over meta-programmeren (naar aanleiding van dit artikel), overwoog ik wat het nut is van method_missing bij het schrijven van domain specific languages (DSL). Niet zo heel veel, dacht ik – het stelt je in staat om in plaats van a.get 'foo' te schrijven: a.foo. Korter, eleganter, maar niet fundamenteel krachtiger. Maar toen bedacht ik het volgende!

class Object
  def method_missing(name, *args, &blk)
    raise NoMethodError unless block_given?
    self.class.send(:define_method, name, &blk)
    self.send(name, *args)
  end
end

Voortaan, wanneer je een nieuwe methode wilt definiƫren en meteen gebruiken, geef je de code gewoon mee als block:

'foo'.chopchop {self.chop.chop} => 'f'

'bar'.chopchop => 'b'

Geplaatst in  | 1 reactie

Reacties

  1. RemVee zei ongeveer 11 uur later:

    Dit is alles behalve elegant en ik kan me (nog) geen situatie voorstellen waar dit niet lomp en onleesbaar is.

    Misschien gaat de leesbaarheid vooruit met:
    class Object
      def method_missing(name, *args, &blk)
        unless name.to_s =~ /^define_and_call_(.+)$/ && block_given?
          raise NoMethodError
        end
        self.send(:define_method, $1, &blk)
        self.send($1, *args)
      end
    end
    
    'foo'.define_and_call_chopchop { self.chop.chop } # => 'f'
    'bar'.chopchop # => 'b'
    

    Eigenlijk vind het ook niet echt bekoorlijk dat, in dit geval, alle strings lijden onder deze impulsieve daad;

    class Object
      def method_missing(name, *args, &blk)
        unless name.to_s =~ /^define_and_call_(.+)$/ && block_given?
          raise NoMethodError
        end
        class << self; self; end.send(:define_method, $1, &blk)
        self.send($1, *args)
      end
    end
    
    'foo'.define_and_call_chopchop { self.chop.chop } # => 'f'
    'bar'.chopchop # => NoMethodError
    

    gebruikt de singleton class en voorkomt dat andere objecten betast worden.

    In dit geval wordt de bruikbaarheid echter ook veel minder. Over het algemeen doe je namelijk niet zoveel operaties op een enkel string object maar werk je steeds met een nieuwe. Het is wel bruikbaar op bijvoorbeeld IO objecten, maar dan had je ook;

    t = 'foo'
    class << t; def chopchop; chop.chop; end; end
    t.chopchop # => 'f'
    

    kunnen schrijven.

    Elegant wil het niet worden.. Heeft java blijvende schade bij me achter gelaten of heeft Michiel te lang in de zon gezeten? ;)

(Laat url/e-mail achter »)

   Voorvertoning