Aufrufen einer Methode aus einer Zeichenfolge mit dem Namen der Methode in Ruby

156

Wie kann ich das tun , was sie reden hier , aber in Ruby?

Wie würden Sie die Funktion für ein Objekt ausführen? und wie würden Sie eine globale Funktion ausführen (siehe die Antwort von jetxee auf den genannten Beitrag)?

BEISPIELCODE:

event_name = "load"

def load()
  puts "load() function was executed."
end

def row_changed()
  puts "row_changed() function was executed."
end 

#something here to see that event_name = "load" and run load()

UPDATE: Wie kommen Sie zu den globalen Methoden? oder meine globalen Funktionen?

Ich habe diese zusätzliche Zeile ausprobiert

puts methods

und load und row_change wurden nicht aufgelistet.

BuddyJoe
quelle

Antworten:

231

Funktionen direkt auf einem Objekt aufrufen

a = [2, 2, 3]
a.send("length")
# or
a.public_send("length")

was wie erwartet 3 zurückgibt

oder für eine Modulfunktion

FileUtils.send('pwd')
# or
FileUtils.public_send(:pwd)

und eine lokal definierte Methode

def load()
    puts "load() function was executed."
end

send('load')
# or
public_send('load')

Dokumentation:

Colin Gravill
quelle
3
+1 Das funktioniert. Dies mag eine dumme Folge sein ... aber warum kann ich das Wort send in der Ruby-Quelle unter - C: \ ruby ​​\ lib \ ruby ​​\ 1.8 \ fileutils.rb nicht finden? Ich dachte, ich würde die Sendefunktion dort finden.
BuddyJoe
1
Ich war neugierig, was es unter der Haube tat.
BuddyJoe
Es ist für das Objekt definiert - ruby-doc.org/core/classes/Object.html#M000332 Ich habe eine Zufallsfunktion für den Zinswert ausgewählt.
Colin Gravill
1
Interessant, denn bevor ich Ihre Antwort zweimal gelesen und vollständig überprüft habe, habe ich FileUtils.send ("load") ausgeführt und meine Funktion ausgeführt. Wenn ich dies also richtig verstehe, indem ich Funktionen in "global" erstelle, füge ich die Methoden dem Stammobjekt hinzu? oder nicht?
BuddyJoe
12
Gut, dass Sie in der Quelle nachgeschlagen haben! :)
Colin Gravill
34

Drei Wege: send/ call/ eval- und ihre Benchmarks

Typischer Aufruf (als Referenz):

s= "hi man"
s.length #=> 6

Verwenden von send

s.send(:length) #=> 6

Verwenden von call

method_object = s.method(:length) 
p method_object.call #=> 6

Verwenden von eval

eval "s.length" #=> 6

 

Benchmarks

require "benchmark" 
test = "hi man" 
m = test.method(:length) 
n = 100000 
Benchmark.bmbm {|x| 
  x.report("call") { n.times { m.call } } 
  x.report("send") { n.times { test.send(:length) } } 
  x.report("eval") { n.times { eval "test.length" } } 
} 

... wie Sie sehen, ist das Instanziieren eines Methodenobjekts die schnellste dynamische Methode zum Aufrufen einer Methode. Beachten Sie auch, wie langsam die Verwendung von eval ist.

#######################################
#####   The results
#######################################
#Rehearsal ----------------------------------------
#call   0.050000   0.020000   0.070000 (  0.077915)
#send   0.080000   0.000000   0.080000 (  0.086071)
#eval   0.360000   0.040000   0.400000 (  0.405647)
#------------------------------- total: 0.550000sec

#          user     system      total        real
#call   0.050000   0.020000   0.070000 (  0.072041)
#send   0.070000   0.000000   0.070000 (  0.077674)
#eval   0.370000   0.020000   0.390000 (  0.399442)

Dank geht an diesen Blog-Beitrag, in dem die drei Methoden etwas näher erläutert werden und in dem auch gezeigt wird, wie überprüft wird, ob die Methoden vorhanden sind.

cwd
quelle
Ich fand das gerade und bemerkte etwas, das nicht abgedeckt war. Was würde ich tun, wenn ich tun wollte Class.send("classVariable") = 5? Das wirft einen Fehler. Gibt es einen Weg daran vorbei? Das Gleiche gilt für die VerwendungClass.method("classVariable").call() = 5
Geheimmeisters
Wenn Sie ein Argument mit einem Send-Aufruf senden möchten, verwenden Sie so etwas wie ClassName.send ("Methodenname", arg1, arg2)
Aleem
Sollte Ihr Benchmark nicht das callInstanziieren des Methodenobjekts ( m.test.method(:length)) umfassen, um dessen wahre Zeit genau darzustellen? Bei Verwendung werden callSie das Methodenobjekt wahrscheinlich jedes Mal instanziieren.
Joshua Pinter
Der Link zum Blog-Beitrag ist übrigens tot.
Joshua Pinter
33

Benutze das:

> a = "my_string"
> meth = a.method("size")
> meth.call() # call the size method
=> 9

Einfach, richtig?

Was das Globale betrifft , denke ich, dass der Ruby-Weg darin besteht, es mit der methodsMethode zu durchsuchen .

Geo
quelle
meth = a.method? Ist das nicht schon die Funktion? Was ist, wenn die Methode etwas zurückgibt?
user1735921
Zu Ihrer Information, wenn die Methode nicht existiert: "my_string".method('blah') #=> NameError: undefined method bla 'für die KlasseString'
Joshua Pinter
3

Persönlich würde ich einen Hash einrichten, um Referenzen zu funktionieren, und dann die Zeichenfolge als Index für den Hash verwenden. Anschließend rufen Sie die Funktionsreferenz mit ihren Parametern auf. Dies hat den Vorteil, dass die falsche Zeichenfolge nicht zulässt, dass etwas aufgerufen wird, das Sie nicht aufrufen möchten. Der andere Weg ist im Grunde evaldie Zeichenfolge. Mach das nicht.

PS: Sei nicht faul und schreibe deine ganze Frage ab, anstatt auf etwas zu verlinken.

Dlamblin
quelle
Es tut uns leid. Ich werde einen Teil des Wortlauts kopieren und übersetzen, um ihn Ruby-spezifisch zu machen. +1
BuddyJoe