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.

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:


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.


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.


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.


Lo siento. Copiaré algunas de las palabras y las traduciré para que sean específicas de Ruby. +1
— BuddyJoe
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.