ruby-mine

exploring the mine

ein neuer Hook: respond_to_missing?

von bovi am 05.10.2009 (01 Uhr)

Seit ein paar Tagen gibt es eine neue Hookmethode mit dem Namen respond_to_missing? im Rubytrunk.

Hooks

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.

respond_to?

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

Ruby Proposal Extension

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

hook off

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

Name (notwendig)

Mail (wird nicht veröffentlicht)

Webseite


Kommentare

  1. Kai schrieb am 06.10.2009 (10 Uhr)

    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.

  2. janfri schrieb am 06.10.2009 (15 Uhr)

    Ach selbst Seiteneffekte könnte ich mir vorstellen: Der Klassiker wäre sicher eine entsprechende Methode zu definieren und true zurückzuliefern. :-)

  3. Skade schrieb am 06.10.2009 (16 Uhr)

    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

  4. Kai schrieb am 06.10.2009 (19 Uhr)

    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?

  5. Skade schrieb am 07.10.2009 (14 Uhr)

    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.

  6. Koraktor schrieb am 15.10.2010 (15 Uhr)

    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.

  7. Skade schrieb am 27.10.2010 (12 Uhr)

    Nein, respond_to_missing verhindert ja gerade, dass respond_to in der Klasse selbst überschrieben werden, was üblicherweise über chaining geschieht.