Geistermethoden in Ruby

Ich mache Sie auf eine Übersetzung eines Fragmentes des Buches Metaprogramming Ruby 2 von Paolo Perrotta aufmerksam.

Was ist method_missing?


In Ruby können wir Methoden aufrufen, die nicht existieren, aber dies wird uns einen Fehler zurückgeben. Beispielsweise:

class Lawyer; end
nick = Lawyer.new
nick.talk_simple
NoMethodError: undefined method 'talk_simple' for #

Erinnern Sie sich, wie die Methodensuche funktioniert? Wenn Sie die talk_simple-Methode aufrufen, wechselt Ruby zur nick-Objektklasse und durchläuft dort die Methoden. Wenn er die Methode dort nicht finden kann, sucht er sie in den Eltern der angegebenen Klasse, dann in Object und schließlich in BasicObject. Nun, da Ruby die talk_simple-Methode nirgendwo finden kann, führt sie die method_missing-Methode für nick aus. Ruby weiß, dass diese Methode vorhanden ist, da es sich um eine private BasicObject-Methode handelt, von der alle Objekte geerbt werden.

Sie können diese Methode zum Experimentieren aufrufen. Da die Methode privat ist, können Sie dies folgendermaßen tun:

nick.send :method_missing, :my_method
NoMethodError: undefine method 'my_method' for #

Sie haben gerade das getan, was Ruby tut, und dem Objekt gesagt: „Ich habe gerade versucht, die Methode my_method aufzurufen, aber sie ist nicht vorhanden. Geben Sie mir daher eine Fehlermeldung.“ BasicObject # method_missing gibt einen NoMethodError zurück, der im Wesentlichen dafür vorhanden ist.

Method_missing override


Höchstwahrscheinlich müssen Sie die Methode method_missing nie selbst aufrufen. Stattdessen können Sie diese Methode neu schreiben, um unbekannte Anrufe abzufangen. Jeder solche Aufruf speichert Informationen über den Namen der Methode und die übergebenen Argumente.

class Lawyer
  def method_missing(method, *args)
    puts "You called: #{method}(#{args.join(', ')})"
    puts "(You also passed it a block)" if block_given?
  end
end
bob = Lawyer.new
bob.talk_simple('a', 'b') do
# a block
end
You called: talk_simple(a, b)
(You also passed it a block)

Durch Überschreiben der Methode method_missing haben Sie die Möglichkeit, Methoden aufzurufen, die nicht beschrieben sind, also nicht existieren. Lassen Sie uns etwas mehr im Detail betrachten.

Ghost-Methoden


Wenn Sie viele ähnliche Methoden deklarieren müssen, können Sie dies vermeiden, indem Sie sie über method_missing aufrufen. So können Sie dem Objekt mitteilen, dass "Wenn eine Methode aufgerufen wird, die nicht vorhanden ist, tun Sie es einfach." Von der Seite des Methodenaufrufs, der method_missing durchläuft, sieht alles wie ein normaler Aufruf einer regulären Methode aus, aber innerhalb der Klasse existiert diese Methode einfach nicht. Dieser Trick wird als Ghost-Methode bezeichnet. Schauen wir uns einige Beispiele an.

Hashie Beispiel


Der Hashie-Edelstein enthält etwas Magie namens Hashie :: Mash. Mash ist eine leistungsfähigere Version der OpenStruct Ruby-Standardbibliothek und ein hashähnliches Objekt, dessen Attribute wie Ruby-Variablen funktionieren. Wenn Sie ein neues Attribut benötigen, deklarieren Sie einfach die Werte für dieses Attribut. So funktioniert es

require 'hashie'
icecream = Hashie::Mash.new
icecream.flavor = 'strawberry'
icecream.flavor                        # => 'strawberry'

Dies funktioniert, weil Hashie :: Mash eine Unterklasse von Ruby Hash ist und diese Attribute im Wesentlichen Geister-Methoden sind. So wird method_missing hier implementiert:

module Hashie
  class Mash< Hashie::Hash
    def method_missing(method_name, *args, &blk)
      return self.[](method_name, &blk) if key?(method_name)
      match = method_name.to_s.match(/(.*?)([?=!]?)$/)
      case match[2]
      when "="
        self[match[1]] = args.first        
        # ...
      else
        default(method_name, *args, &blk)
      end
    end
    # ...
  end
end

Wenn der Name der aufgerufenen Methode der Schlüsselname im Hash ist (wie Flavor), ruft Hashie :: Mash # method_missing einfach die Methode [] auf, um den entsprechenden Wert zurückzugeben. Wenn der Name mit "=" endet, schneidet method_missing die zusätzlichen Zeichen ab und ruft den Wert ab, der gespeichert werden muss. Wenn der Name nichts anderes enthält, gibt method_missing einfach einen Standardwert zurück. Hashie :: Mash enthält auch andere Sonderzeichen wie "?".

Antwort auf Abwesenheit?


Wenn Sie mit reply_to? Eine gespenstische Methode testen möchten, dann liegt Rubys offensichtliche Angelegenheit bei Ihnen.

john = Lawyer.new
john.respond_to?(:talk_simple)        # => false

Dies kann problematisch sein, da wir reply_to häufig für Überprüfungen verwenden können. Aber Ruby bietet einen guten Mechanismus, um Freunde dazu zu bringen, auf Geister- und Antwortmethoden zu reagieren.

reply_to? ruft die reply_to_missing-Methode auf? Dies gibt true zurück, wenn es sich um eine gespenstische Methode handelt. Um sicherzustellen, dass dies während der Entwicklung keine weiteren Probleme verursacht, müssen Sie jedes Mal method_missing und reply_to_missing neu definieren.

class Lawyer
  # ...
  def respond_to_missing?(method, include_private = false)
    methods.include?(method) || super
  end
end
bill = Lawyer.new
bill.respond_to?(:talk_simple)          # => true

Ein wenig früher haben Rubisten reply_to selbst neu definiert? Methode. Aber jetzt gibt es reply_to_missing? und übersteuern reply_to? Es wird als nicht die richtige Option für die Arbeit mit method_missing angesehen.

Ghost-Methoden sind nur einige der interessanten Funktionen in Ruby.

Jetzt auch beliebt: