Llamar a un método desde una cadena con el nombre del método en Ruby

157

¿Cómo puedo hacer de lo que están hablando aquí , pero en Ruby?

¿Cómo harías la función en un objeto? y ¿cómo haría una función global (vea la respuesta de jetxee en la publicación mencionada)?

CÓDIGO DE EJEMPLO:

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

ACTUALIZACIÓN: ¿Cómo se llega a los métodos globales? o mis funciones globales?

Probé esta línea adicional

puts methods

y cargar y remar_cambiar donde no figura.

BuddyJoe
fuente

Respuestas:

232

Para llamar a funciones directamente en un objeto

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

que devuelve 3 como se esperaba

o para una función de módulo

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

y un método definido localmente

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

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

Documentación:

Colin Gravill
fuente
3
+1 Eso funciona. Esto puede ser un seguimiento tonto ... pero ¿cómo es que no puedo encontrar la palabra enviada en la fuente de Ruby en - C: \ ruby ​​\ lib \ ruby ​​\ 1.8 \ fileutils.rb? Pensé que encontraría la función de envío allí.
BuddyJoe
1
Tenía curiosidad por lo que estaba haciendo bajo el capó.
BuddyJoe
Está definido en el objeto: ruby-doc.org/core/classes/Object.html#M000332 Elegí una función aleatoria para el valor del interés.
Colin Gravill
1
Interesante porque antes de leer su respuesta dos veces, y la asimilé por completo, ejecuté FileUtils.send ("load") y ejecutó mi función. así que si entiendo esto correctamente creando funciones en "global", ¿estoy agregando los métodos al objeto raíz? ¿o no?
BuddyJoe
12
Bien por ti para buscar cosas en la fuente! :)
Colin Gravill
34

Tres formas: send/ call/ eval- y sus puntos de referencia

Invocación típica (para referencia):

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

Utilizando send

s.send(:length) #=> 6

Utilizando call

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

Utilizando eval

eval "s.length" #=> 6

 

Puntos de referencia

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

... como puede ver, crear instancias de un objeto de método es la forma dinámica más rápida de llamar a un método, también observe cuán lento es el uso de 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)

El crédito va a esta publicación de blog que elabora un poco más sobre los tres métodos y también muestra cómo verificar si los métodos existen.

cwd
fuente
Estaba encontrando esto, y noté algo que no estaba cubierto. ¿Qué haría si quisiera hacer Class.send("classVariable") = 5? Eso arroja un error. ¿Hay alguna forma de evitar eso? Lo mismo es cierto para usarClass.method("classVariable").call() = 5
thesecretmaster
si desea enviar algún argumento con send call use algo como esto ClassName.send ("method_name", arg1, arg2)
Aleem
¿No debería su punto de referencia para callincluir la creación de instancias del método object ( m.test.method(:length)) para representar con precisión su tiempo real? Cuando lo use call, probablemente creará una instancia del objeto del método cada vez.
Joshua Pinter
El enlace de la publicación del blog está muerto, por cierto.
Joshua Pinter
33

Utilizar este:

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

Simple, ¿verdad?

En cuanto a lo global , creo que la forma de Ruby sería buscarlo usando el methodsmétodo.

Geo
fuente
meth = a.method? ¿No es esto llamar a la función ya? ¿Qué pasa si el método devuelve algo?
user1735921
FYI para cuando el método no existe: "my_string".method('blah') #=> NameError: undefined method bla 'para la claseString'
Joshua Pinter
3

Personalmente, configuraría un hash para que funcionen las referencias y luego usaría la cadena como un índice para el hash. Luego llama a la referencia de función con sus parámetros. Esto tiene la ventaja de no permitir que la cadena incorrecta llame a algo que no desea llamar. La otra forma es básicamente evalla cadena. No hagas esto.

PD: no seas perezoso y escribe toda tu pregunta, en lugar de vincularte a algo.

dlamblin
fuente
Lo siento. Copiaré algunas de las palabras y las traduciré para que sean específicas de Ruby. +1
BuddyJoe