2017-07-10 2 views
4

Ich habe versucht, eine Klasse zu erstellen, die über eine private Klassenmethode verfügt. Ich möchte, dass diese private Klassenmethode innerhalb einer Instanzmethode verwendet werden kann.Was bedeutet `& method (: method_name)` in Ruby?

Nachfolgend war mein erster Versuch:

class Animal 
    class << self 
    def public_class_greeter(name) 
     private_class_greeter(name) 
    end 

    private 
    def private_class_greeter(name) 
     puts "#{name} greets private class method" 
    end 
    end 

    def public_instance_greeter(name) 
    self.class.private_class_greeter(name) 
    end 
end 

Animal.public_class_greeter('John') funktioniert gut, John greets private class method Druck.

Jedoch gibt Animal.new.public_instance_greeter("John") einen Fehler aus: NoMethodError: private method 'private_class_greeter' called for Animal:Class.

Das wird erwartet, da der Aufruf dasselbe ist wie Animal.private_class_greeter, was offensichtlich einen Fehler auslöst.

Nach der Suche, wie man dieses Problem behoben werden kann, kam ich mit dem folgenden Code auf, dass der Job nicht:

class Animal 
    class << self 
    def public_class_greeter(name) 
     private_class_greeter(name) 
    end 

    private 
    def private_class_greeter(name) 
     puts "#{name} greets private class method" 
    end 
    end 

    define_method :public_instance_greeter, &method(:private_class_greeter) 
end 

ich nicht genau verstehen, was hier passiert: &method(:private_class_greeter).

Könnten Sie bitte erklären, was das bedeutet?

Wenn ich ersetzen:

define_method :public_instance_greeter, &method(:private_class_greeter) 

mit:

def public_instance_greeter 
    XYZ 
end 

dann, was der Inhalt anstelle von XYZ sein sollte?

Antwort

8

Wie analysiert Ruby &method(:private_class_greeter)?

Der Ausdruck &method(:private_class_greeter) ist

  • der Wert des Methodenaufrufes method(:private_class_greeter)
  • mit dem Operator& vorangestellt.

Was bedeutet die method Methode tun?

Die Methode method sucht den angegebenen Methodennamen im aktuellen Kontext und gibt ein Method Objekt zurück, das es darstellt. Beispiel in irb:

def foo 
    "bar" 
end 

my_method = method(:foo) 
#=> #<Method: Object#foo> 

Sobald Sie diese Methode haben, können Sie mit ihm verschiedene Dinge tun:

my_method.call 
#=> "bar" 

my_method.source_location # gives you the file and line the method was defined on 
#=> ["(irb)", 5] 

# etc. 

Was ist der & Betreiber?

Der & Bediener verwendet wird, eine Proc als Block auf ein Verfahren übergeben, die einen Block an sie übergeben werden, erwartet. Außerdem ruft es implizit die to_proc-Methode für den übergebenen Wert auf, um Werte, die nicht Proc sind, in Proc zu konvertieren.

Die Klasse Method implementiert to_proc — Sie gibt den Inhalt der Methode als Proc zurück. Daher können Sie eine Method Instanz mit & Präfix und es als Block an einem anderen Methode übergeben:

def call_block 
    yield 
end 

call_block &my_method # same as `call_block &my_method.to_proc` 
#=> "bar" 

Die define_method Methode geschieht nur einen Block mit dem Inhalt des neuen Verfahrens zu nehmen, die definiert wird. In Ihrem Beispiel übergibt &method(:private_class_greeter) die bestehende private_class_greeter Methode als Block.


Ist dies, wie &:symbol funktioniert?

Ja. Symbol implementiert to_proc so dass Sie Ihren Code wie folgt vereinfachen:

["foo", "bar"].map(&:upcase) 
#=> ["FOO", "BAR"] 

# this is equivalent to: 
["foo", "bar"].map { |item| item.upcase } 

# because 
:upcase.to_proc 

# returns this proc: 
Proc { |val| val.send(:upcase) } 

Wie kann ich &method(:private_class_greeter) replizieren?

Sie können in einem Block übergeben, die die Ziel-Methode aufruft:

define_method :public_instance_greeter do |name| 
    self.class.send(:private_class_greeter, name) 
end 

Natürlich, dann brauchen Sie nicht define_method mehr zu verwenden, die in der gleichen Lösung führen Eric in his answer erwähnt:

def public_instance_greeter(name) 
    self.class.send(:private_class_greeter, name) 
end 
+1

Schön erklärt. –

+2

'&' ** übergibt keine 'Method'-Instanz als Block. Es unterscheidet den Parameter überhaupt nicht nach Typ. Es ruft implizit 'to_proc' auf einem Empfänger auf (in diesem Fall 'Method'-Instanz) und übergibt das Ergebnis (' Proc'-Instanz) an die Methode. Absolut das gleiche wie '&: to_s' ruft' Symbol # to_proc' auf ': to_s' Symbol auf und übergibt das' Proc' weiter. – mudasobwa

+0

Ich vereinfachte die Erklärung absichtlich. Aber ich denke, ich kann etwas über 'to_proc' hinzufügen ... –

3

Zuerst nehmen Sie gut mit Ihrer Einrückung. private sollte 2 Leerzeichen auf der rechten Seite sein: es macht den Eindruck, dass public_instance_greeter sonst privat ist.

Wenn Sie nicht über Verkapselung ist es egal, könnten Sie einfach Kernel#send verwenden:

class Animal 
    class << self 
    def public_class_greeter(name) 
     private_class_greeter(name) 
    end 

    private 
    def private_class_greeter(name) 
     puts "#{name} greets private class method" 
    end 
    end 

    def public_instance_greeter(name) 
    self.class.send(:private_class_greeter, name) 
    end 
end 

Animal.public_class_greeter('John') 
# John greets private class method 
Animal.new.public_instance_greeter("John") 
# John greets private class method 
+1

Vielen Dank für Ihren Vorschlag zum Einzug. Ich kenne populäre Style Guides. Ich denke, negatives Einrücken "private" Schlüsselwort hilft, leicht zu identifizieren, wo private Methoden beginnen. Aus diesem Grund ziehe ich es vor, das Schlüsselwort 'private' in die root-Ebene einzurücken. Ich weiß, dass dies gegen die meisten Stilrichtlinien ist. Denkst du mein Denkprozess macht Sinn? –

+1

@SatyaramBV: Wenn Sie (und Ihre Kollegen) es lesen können, ist das für mich in Ordnung. Eine Möglichkeit, dieses Problem zu vermeiden, wäre es, die Klasse "self" an das Ende der Klasse "Animal" zu verschieben. –

+1

@SatyaramBV FWIW, alle Teams, an denen ich gearbeitet habe, haben auch die ausgereiften Eingabehilfe-Schlüsselwörter bevorzugt. – coreyward