2016-06-02 7 views
0
class Person 
    include Voice 
    include Beep 
end 

module Voice 
    def speak 
    puts 'speaking from module' 
    end 

    def original_speak 
    # ??? 
    end 
end 

module Beep 
    def speak 
    puts 'beep beep' 
    end 
end 

Wie anrufen? Voice#speak? Zum Beispiel:Wie wird eine Instanzmethode aufgerufen, die in einem Modul definiert ist, das von einem anderen Modul in Ruby überschrieben wird?

Ich möchte diesen Code zum Voice-Modul hinzufügen, nicht die Klasse Person oder das Beep-Modul.

Anwendungsfall:
Ein Modul, das weich löschen ist:

module Undeletable 
    def delete 
    # mark document as deleted. This creates a deletion document. 
    end 

    def restore 
    # Delete (for real) the deletion. 
    end 

    def obliterate 
    restore # because we don't want orphaned deletions. 

    real_delete # This should call Mongoid#delete 
    end 
end 

def Foo 
    include Mongoid::Document 
    include Undeletable 
end 

So im Allgemeinen, wenn wir foo.delete nennen, möchten wir weichen löschen. In seltenen Fällen möchten wir jedoch ein echtes Löschen durchführen. Das Modul sollte beide Methoden unterstützen.

+0

Wenn es nicht leicht gemacht werden kann, dann könnte es falsch sein, es zu tun. Was ist dein Anwendungsfall? –

+0

Was ist los? Sie haben die Frage geändert und beide Antworten unsinnig gemacht. Ich schlage vor, du rollst zurück. Deine ursprüngliche Frage war gut, interessant. (Downvote nicht meins.) –

+0

Haha, ich dachte, jemand würde fragen. Ihre Antwort wurde gelöscht und ich habe die Antwort von Pascal bereits implementiert. Die Antwort hat bei mir nicht funktioniert, weil die Frage anders war. –

Antwort

1

Leser: Ich habe diese Antwort auf die ursprüngliche Frage geschrieben. Die Frage wurde nachträglich geändert. Wenn Sie interessiert sind, überprüfen Sie den Bearbeitungsverlauf für den Kontext.

Die Schritte sind wie folgt.

  • einen Rückruf in Modul erstellen Voice, die aufgerufen wird, wenn das Modul in Person enthalten ist. Die (Klassen-) Methode dafür ist Module#included, die ein Argument hat, die Klasse einschließlich des Moduls.
  • Included erstellt einen Alias ​​original_speak von Person.speak (in Person). Dies muss zuerst erfolgen, während Person#speak die ursprünglich auf Person definierte Methode speak ist.
  • Included entfernt dann die (original) Instanzmethode Person#speak, Module#remove_method (nicht Module#undef_method) verwendet wird, wodurch Person#speakVoice#speak die Instanzmethode zu werden, was in der Tat war, richtig „hinter“ die ursprünglichen Person#speak vor die Entfernung des letzteren.

module Voice 
    def self.included(klass) 
    klass.send(:alias_method, :original_speak, :speak) 
    klass.send(:remove_method, :speak) 
    end 
    def speak 
    puts 'speaking from module' 
    end 
end 

class Person 
    def speak 
    puts 'speaking from class' 
    end 
    include Voice 
end 

Person.instance_method(:speak).owner 
    #=> Voice 
Person.instance_method(:original_speak).owner 
    #=> Person 
Person.ancestors 
    #=> [Person, Voice, Object, Kernel, BasicObject] 

person = Person.new 

person.original_speak 
speaking from class 

person.speak 
speaking from module 
+0

Wenn sowohl das enthaltene Modul als auch die Klasse dieselbe Methode enthalten, wird die in der Klasse definierte verwendet. Es ist weder verloren noch überschrieben. –

+0

@pascalbetz, ja, danke. Leser: Pascal, bezog sich auf eine falsche Erklärung, die ich für eine Antwort gab, die ich später revidierte. –

1

Ich sehe die einfachste Möglichkeit, einen Parameter an die Funktion hinzuzufügen, als die delete überschreibt. Zum Beispiel:

module Undeletable 
    def delete(call_super=false) 
    return super if call_super 
    return "some custom response" 
    end 

    def obliterate 
    delete(true) 
    end 
end 

Das Schlüsselwort super ist hier wichtig. Wenn eine Methode von einer anderen überschrieben wird, kann das Original von super aufgerufen werden. Es ist auch möglich, Argumente an super zu übergeben.