Chiamare un metodo da una stringa con il nome del metodo in Ruby


157

Come posso fare quello di cui stanno parlando qui , ma in Ruby?

Come faresti la funzione su un oggetto? e come faresti una funzione globale (vedi la risposta di jetxee sul post citato)?

CODICE DI ESEMPIO:

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()

AGGIORNAMENTO: Come si arriva ai metodi globali? o le mie funzioni globali?

Ho provato questa linea aggiuntiva

puts methods

e carica e row_change dove non elencato.

Risposte:


232

Per chiamare le funzioni direttamente su un oggetto

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

che restituisce 3 come previsto

o per una funzione del modulo

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

e un metodo definito localmente

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

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

Documentazione:


3
+1 Funziona. Questo potrebbe essere un stupido follow-up ... ma come mai non riesco a trovare la parola invia nel sorgente Ruby su - C: \ ruby ​​\ lib \ ruby ​​\ 1.8 \ fileutils.rb? Ho pensato di trovare la funzione di invio lì dentro.
BuddyJoe,

1
Ero curioso di quello che stava facendo sotto il cofano.
BuddyJoe,

È definito sull'oggetto - ruby-doc.org/core/classes/Object.html#M000332 Ho scelto una funzione casuale per il valore degli interessi.
Colin Gravill,

1
Interessante perché prima di leggere due volte la tua risposta, e di averla completamente analizzata, ho eseguito FileUtils.send ("load") e ha eseguito la mia funzione. quindi se lo capisco correttamente creando funzioni in "globale" sto aggiungendo i metodi sull'oggetto root? o no?
BuddyJoe,

12
Buon per te per cercare cose nella fonte! :)
Colin Gravill,

34

Tre modi: send/ call/ eval- e relativi parametri di riferimento

Invocazione tipica (per riferimento):

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

utilizzando send

s.send(:length) #=> 6

utilizzando call

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

utilizzando eval

eval "s.length" #=> 6

 

Punti di riferimenti

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" } } 
} 

... come puoi vedere, creare un'istanza di un oggetto metodo è il modo più veloce e dinamico per chiamare un metodo, nota anche quanto sia lento usare eval.

#######################################
#####   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)

Il merito va a questo post sul blog che elabora un po 'di più sui tre metodi e mostra anche come verificare se i metodi esistono.


Lo stavo solo trovando e ho notato qualcosa che non era coperto. Cosa farei se volessi farlo Class.send("classVariable") = 5? Questo genera un errore. C'è un modo per aggirare questo? La stessa cosa vale per l'usoClass.method("classVariable").call() = 5
segretario

se vuoi inviare qualche argomento con send call usa qualcosa come questo ClassName.send ("method_name", arg1, arg2)
Aleem

Il tuo benchmark non dovrebbe callincludere un'istanza dell'oggetto method ( m.test.method(:length)) per rappresentare con precisione il suo tempo reale? Quando si utilizza callè probabile che si crei un'istanza dell'oggetto metodo ogni volta.
Joshua Pinter,

Il link al post sul blog è morto, a proposito.
Joshua Pinter,

33

Usa questo:

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

Semplice vero?

Per quanto riguarda il globale , penso che il modo in cui Ruby sarebbe quello di cercarlo usando il methodsmetodo.


meth = a.method? questa chiamata non è già la funzione? cosa succede se il metodo restituisce qualcosa?
user1735921

Cordiali saluti per quando il metodo non esiste: "my_string".method('blah') #=> NameError: undefined method blah 'for classString'
Joshua Pinter

3

Personalmente vorrei impostare un hash per far funzionare i riferimenti e quindi usare la stringa come indice per l'hash. Quindi si chiama il riferimento della funzione con i suoi parametri. Ciò ha il vantaggio di non consentire alla stringa errata di chiamare qualcosa che non si desidera chiamare. L'altro modo è fondamentalmente evalla stringa. Non farlo.

PS non essere pigro e in realtà scrivi tutta la tua domanda, invece di collegarti a qualcosa.


Scusate. Copierò alcune delle parole e le tradurrò per renderle specifiche per Ruby. +1
BuddyJoe,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.