2014-01-16 16 views
6

Gibt es in Ruby eine Möglichkeit, eine Methode aufzurufen, wenn eine andere Methode der Klasse aufgerufen wird? Zum BeispielWie man die Methode einer Klasse aufruft, wenn eine andere Methode der Klasse aufgerufen wird (Ruby)

class Car 
    def repair 
     puts "Repaired!" 
    end 
    def drive 
     # content 
    end 
    def checkup 
     # content 
    end 
end 

In diesem Beispiel, wenn ich auf einer Instanz von Car jede Methode aufrufen, sollte ich die repair Methode immer aufrufen. Wie mache ich das in Ruby?

HINWEIS: Ich auch tunrepair in integrierten Methoden genannt wollen, auch, wie Carinstance.classrepair auch nennen sollte.

+0

Kurz vor dem Aufruf von 'repair' in jeder Methode? – Kal

+0

ja. Und für eingebaute Methoden wie ".class" sollte es 'repair' dort nennen, to. Die Klasse "Car" ist nur ein Beispiel für das, was ich brauche. – crownusa

+0

Warum willst du das für eingebaute Methoden? – Shoe

Antwort

7

Ich habe angenommen, dass Sie Car#repair aufgerufen werden sollen, nachdem jede andere Car andere Instanz Methoden zurückgegeben haben. Ich sehe, dass Sie eine Anforderung hinzugefügt haben, die andere Methoden auch aufrufen repair. Ich habe am Ende einige Anmerkungen hinzugefügt, um dies auf integrierte Instanzmethoden auszudehnen.

Der Ansatz, den ich gemacht habe, ist die Verwendung von BasicObject#method_missing zu machen:

class Car 
    def repair 
    puts "Repaired!" 
    end 

    def drive 
    puts "Drive!" 
    end 

    def checkup 
    puts "Checkup!" 
    end 

    def method_missing(m, *args) 
    if @@ims.key?(m) 
     ret = send(@@ims[m], *args) 
     repair 
     ret 
    else 
     super 
    end 
    end 

@@ims = instance_methods(false).each_with_object({}) do |m,h| 
    next if (m == :repair || m == :method_missing) 
    saved_name = "_#{m}" 
    alias_method saved_name, m 
    h[m] = saved_name 
    remove_method(m) 
    end    
end 

car = Car.new 

car.repair 
Repaired! 

car.drive 
Drive! 
Repaired! 

car.checkup # 
Checkup! 
Repaired! 

car.wash # => in `method_missing': undefined method `wash'... 

Wenn Klasse Car analysiert wird, nachdem alle Instanzmethoden konstruiert worden sind, werden die folgenden Operationen sind durchgeführt, was ich anhand eines Beispiels erkläre:

instance_methods(false) # => [:repair, :drive, :checkup, :method_missing] 

each_with_object({}) erzeugt einen Hash (zunächst leer), auf den sich die Blockvariable h bezieht (mehr dazu später).

next if (m == :repair || m == :method_missing) 

:repair und :method_missing verursacht werden übersprungen.

Wenn m => :drive die folgenden drei Aussagen effektiv :drive-:_drive umbenennen und fügen :drive" => "_drive" zu dem Hash-h.

each_with_object kehrt

@@ims = {:drive=>"_drive", :checkup=>"_checkup"} 

und jetzt

instance_methods(false) # => [:repair, :method_missing, :_drive, :_checkup] 

Da es nicht mehr ist eine Methode :drive, Car.new.drive ruft method_missing(:drive). Letzteres findet, dass @@ims einen Schlüssel :drive hat, so verwendet es send, um :_drive aufzurufen, ruft :repair auf und gibt den Rückgabewert :_drive zurück. Wenn method_missing übergeben wird, wird eine Methode aufgerufen, die kein Schlüssel von @@ims ist, super wird aufgerufen, und eine Ausnahme wird ausgelöst.

In einer jetzt entfernten Bearbeitung schlug ich vor, integrierte Instanzmethoden einzubeziehen, man muss nur instance_methods(false) in instance_methods ändern, aber vor möglichen unbeabsichtigten Nebenwirkungen gewarnt werden. @Kal wies darauf hin, dass integrierte Instanzmethoden nicht entfernt werden können, so dass der Ansatz nicht funktioniert.Das ist auch gut - man sollte sich auf diese Weise nicht mit Ruby anlegen. Ich habe meine Behauptung offensichtlich nicht geprüft. Schande!

+0

Brilliant! Kannst du es ändern, um 'instance_methods (true)' zu verwenden, damit es auch eingebaute Methoden enthält? – Kal

+0

@Kal, Ihr Wunsch ist mein Befehl. –

+1

Es sieht so aus, als ob remove_method (m) nicht für integrierte (geerbte) Methoden funktioniert. Ich bekomme den Fehler "NameError: Methode 'nil?' nicht im Auto definiert ". Zeit, dem OP vielleicht vorzuschlagen, dass das Herumspielen mit Methoden in der Klasse "Object" keine gute Idee ist, oder gibt es einen Workaround? – Kal

1
class Car 
    def self.default_method 
     instance_methods(true).each do |meth| 
     alias_method meth, :repair 
     end 
    end 
    def initialize 
    self.class.default_method 
    end 

    def repair 
    puts "Repaired!" 
    end 
    def drive 
    # content 
    end 
    def checkup 
    # content 
    end 
end 

car = Car.new 

car.drive # => Repaired! 
car.checkup # => Repaired! 
car.class # => Repaired! 

Beachten Sie, dass die integrierten Methoden neu zu definieren einige Warnungen generiert:

# => untitled 5:6: warning: redefining `object_id' may cause serious problems 
# => untitled 5:6: warning: redefining `__send__' may cause serious problems 

Edit: Oops, ich gepostet dies zu schnell und vor Ort das Problem nicht. Es ruft repair, aber nicht die ursprünglichen Methoden auf. Ich wusste, dass es zu einfach schien! Ich denke, ich bin mit diesem hier überfordert. :-) (Anmerkung: Ich dachte Cary's Ansatz war wirklich clever, und es funktioniert für Ihre eigenen Methoden, aber es sieht so aus, als ob er mit den eingebauten Methoden eine Sackgasse beendet hätte und auf jeden Fall Methoden verändert dass Sie wirklich nicht mit eingebauten Methoden versuchen sollten).

+0

Hey @Kal, ich mag deine Antwort, ich habe es versucht, es funktioniert perfekt, bis auf ein Problem: Ich möchte 'Reparatur' genannt ** zusätzlich zum ursprünglichen Methodeninhalt. Beispiel: 'car.methods' sollte' repair' aufrufen, aber trotzdem eine Reihe von Methoden zurückgeben, und 'car.class' sollte' repair' aufrufen, zusätzlich zur Rückgabe der Klasse auch. Danke für Ihre Hilfe! – crownusa

+0

Ja, Entschuldigung, ich habe erwähnt, dass meine Antwort in der Bearbeitung unten nicht richtig funktioniert. Ich habe zu schnell gepostet und habe es erst gar nicht bemerkt, weil die Laufwerks- und Prüfmethoden nichts ausgeben. Ich hätte auch den Kommentar "Aber es funktioniert!" Löschen sollen, was ich jetzt machen werde. :-) – Kal

Verwandte Themen