Seit ein paar Tagen gibt es eine neue Hookmethode mit dem Namen respond_to_missing? im Rubytrunk.
Nach initialize ist wohl method_missing eine der am häufigsten verwendeten Methodenhooks die es in Ruby gibt. Ich würde einen Hook als einen Zwischenstop innerhalb einer Deklaration betrachten. Tritt ein bestimmtes Ereignis ein, nimmt der Interpreter einen Schleichweg über den Methodenhook des entsprechenden Objektes.
Ein new auf ein Standardobjekt, verwendet im Hintergrund z.B den Hook initialize.
1 2 3 4 5 6 7 8 9 10 11 |
class MeinKettchen < String; end MeinKettchen.new # => "" class MeinKettchen < String; def initialize puts self.class end end MeinKettchen.new # MeinKettchen # => "" |
Wird eine Methode verwendet, die in einem bestimmten Objekt nicht deklariert ist, wird method_missing aufgerufen:
1 2 3 4 5 6 7 8 9 10 |
s = "string" s.chunk # => NoMethodError: undefined method `chunk' for "string":String def s.method_missing args puts args.inspect end s.chunk # :chunk # => false |
Dieses Feature wird z.B. von ActiveRecord verwendet. Statt für jede Spalte eine seperate Methode definieren zu müssen, werden einfach sämtliche unbekannte Methodenaufrufe umgeleitet und auf die Datenbank abgesetzt.
Anstatt nun auf die NoMethodError Exception zu reagieren, verwenden (relativ wenige Leute) die Methode respond_to?. Diese fragt ein bestimmtes Objekt ab, ob es auf eine Methode antwortet:
1 2 3 |
s = "string" s.respond_to? :chomp # => true s.respond_to? :chunky # => false |
Der neue Hook respond_to_missing? hängt sich an genau dieser Stelle ein. So wird im Falle einer negativen Anfrage mit respond_to? der entsprechende Hook ausgeführt:
1 2 3 4 5 6 7 8 9 10 11 12 |
s = "string" s.respond_to? :chomp # => true s.respond_to? :chunky # => false def s.respond_to_missing? args puts args.inspect end s.respond_to? :chomp # => true s.respond_to? :chunky # :chunky # => false |
Damit können wir jetzt einzelne Objekte um eine interaktive Proposal Funktion ergänzen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
s = "My poetry" def s.respond_to_missing? args met = "#{self.class}##{args}" puts "You seem to require #{met}" puts "Please input why you need this method: " puts proposal = gets puts # E-Mail an Ruby Core # not implemented (YET!) puts "Your proposal: " puts "\tSubject: Please implement #{met}" puts "\tMessage: #{proposal}" puts "was (not) sent to Ruby-Core." end s.respond_to? :rhyme? # You seem to require String#rhyme? # Please input why you need this method: # # >> I like to check if my lyrics are sounding nice. # # Your proposal: # Subject: Please implement String#rhyme? # Message: I like to check if my lyrics are sounding nice. # was (not) sent to Ruby-Core. # => false |
Im Moment scheint diese Funktionalität noch nicht ganz stabil zu sein. Einige SystemStackErrors und Illegal instruction Exceptions sind mir beim Spielen über den Weg gelaufen. Nachdem diese Dinge bereinigt sind, bin ich aber schon sehr gespannt ob und wenn ja was, andere Leute mit diesem Feature anstellen. Hat jemand Ideen?
Kommentar schreiben
Kommentare
Hm, die ?-Methoden waren für mich immer rein funktionale Abfragemethoden. Darum finde ich es leicht befremdlich, dort irgendeinen Seiteneffekt einzubauen. Mir fällt spontan auch keine Anwendung dafür ein… Ansonsten freu ich mich über jeden neuen Hook. Ein “initialize” für Literale fände ich aber interessanter und nützlicher.
Ach selbst Seiteneffekte könnte ich mir vorstellen: Der Klassiker wäre sicher eine entsprechende Methode zu definieren und true zurückzuliefern. :-)
Naja, der gewünschte Einsatzzweck ist natürlich klar: man kann damit deklarieren, dass man auf einen Selektor dynamisch über method_missing antwortet. Für Delegatoren ist das extrem praktisch. Die brauchen dann nämlich nicht mehr die (interne) Methode respond_to? zu zerfleddern.
Für was anderes sollte man es tunlichst nicht einsetzen.
Gruß, Skade
Hm, was heißt respond_to? zerpflücken. Da setzt man ein (super or my_own_respond_to?(sym)) rein und es müsste dasselbe rauskommen wie bei diesem Hook. Oder überseh ich da was?
Ja, das wäre aber method chaining. Das ist eine Technik, die vom Core zwar unterstützt, aber nicht gerne gesehen wird. Ich mags auch nicht. Ich finde die Variante über eine Standardimplementierung und einen Hook besser.
Aber respond_to_missing? führt doch u.U. auch zu Method Chaining. Ich glaube es geht hier jedoch auch darum zu spezifizieren, ob eine Methode von Haus aus implementiert ist oder durch dynamisches Handling unterstützt wird. Das ist dann eher für “Endanwender” interessant, weil der so rausfinden kann, was Sache ist und dann entscheiden kann, ob er die Methode aufrufen will. Das ist nämlich mit respond_to? allein nicht möglich.
Nein, respond_to_missing verhindert ja gerade, dass respond_to in der Klasse selbst überschrieben werden, was üblicherweise über chaining geschieht.