¿Qué hace send () en Ruby?

94

¿Alguien puede decirme qué?

send("#{Model.find...}")

es y hace?

Banquero cristiano
fuente
2
¿Es esta toda la línea de código? Quiero decir, ¿no hay nada antes de 'enviar'?
jirafa

Respuestas:

107

send envía un mensaje a una instancia de objeto y sus ancestros en la jerarquía de clases hasta que algún método reacciona (porque su nombre coincide con el primer argumento).

Prácticamente hablando, esas líneas son equivalentes:

1.send '+', 2
1.+(2)
1 + 2

Tenga en cuenta que sendomite las comprobaciones de visibilidad, por lo que también puede llamar a métodos privados (útil para pruebas unitarias).


Si realmente no hay una variable antes del envío, eso significa que se usa el Objeto global:

send :to_s    # "main"
send :class   # Object
jirafa
fuente
1
Oh, ya veo, entonces uno podría usar enviar si uno quisiera almacenar algo como 1 mes en la base de datos en lugar de decir estáticamente el número de días.
Christian Bankester
3
Es cierto que podría usarlo para llamar a métodos con nombres calculados, no estáticos. (Sin embargo, no debe permitir la entrada del usuario sin restricciones para evitar llamar a métodos privados ... Sin embargo, podría darles un prefijo único: enviar 'user_method _' + nombre de método, * args)
giraff
2
Un buen caso de uso podría ser si desea probar un método de clase protegida, podría llamarlo fuera de una clase, en un archivo de prueba ...
GN.
106

send es un método ruby ​​(sin rieles) que permite invocar otro método por su nombre.

De la documentación

   class Klass
     def hello(*args)
       "Hello " + args.join(' ')
     end
   end
   k = Klass.new
   k.send :hello, "gentle", "readers"   #=> "Hello gentle readers"

http://corelib.rubyonrails.org/classes/Object.html#M001077

Nikita Rybak
fuente
5
Gran respuesta, más clara que la verbosa respuesta aceptada.
codificación aaron
63

Una de las características más útiles que creo con el método .send es que puede llamar dinámicamente al método. Esto puede ahorrarle mucho escribir. Uno de los usos más populares del método .send es asignar atributos de forma dinámica. Por ejemplo:

class Car
  attr_accessor :make, :model, :year
end  

Para asignar atributos con regularidad, se necesitaría

c = Car.new
c.make="Honda"
c.model="CRV"
c.year="2014"

O usando el método .send:

c.send("make=", "Honda")
c.send("model=", "CRV")
c.send("year=","2014")

Pero todo se puede reemplazar con lo siguiente:

Suponiendo que su aplicación Rails necesita asignar atributos a su clase de automóvil desde la entrada del usuario, puede hacerlo

c = Car.new()
params.each do |key, value|
  c.send("#{key}=", value)
end
Antonio Jha
fuente
Gracias por el gran enlace
sid_09
7
El uso de .send de esta manera agrega una complejidad innecesaria y facilita la introducción inadvertida de un error en el código. Por ejemplo, en su código anterior, si agrega una nueva entrada a su hash de parámetros (como 'cilindros'), el código fallará con un error de método indefinido.
Kevin Schwerdtfeger
1
¿responder a? podría utilizarse para evitar tales errores, si se desea.
Richard_G
1
¡Esta fue una gran explicación! ¡Gracias Jha!
Sharath
1
@Kevin tienes razón, pero a veces puede ser necesario. Más flexibilidad se correlaciona con más riesgo, que puede mitigarse.
Will Sheppard
12

Otro ejemplo, similar al https://stackoverflow.com/a/26193804/1897857 de Antonio Jha

es si necesita leer atributos en un objeto.

Por ejemplo, si tiene una matriz de cadenas, si intenta recorrerlas y llamarlas en su objeto, no funcionará.

atts = ['name', 'description']
@project = Project.first
atts.each do |a|
  puts @project.a
end
# => NoMethodError: undefined method `a'

Sin embargo, puede sendlas cadenas al objeto:

atts = ['name', 'description']
@project = Project.first
atts.each do |a|
  puts @project.send(a)
end
# => Vandalay Project
# => A very important project
Mike Vallano
fuente
¡Gracias por una explicación simple y fácil!
Junan Chakma
¡Gracias! Esa es exactamente la respuesta que busco. ¿Se pregunta si esto se usa comúnmente? Me encontré con algo similar en el código heredado, no estoy seguro de poder seguir con él. @ Mike Vallano
B Liu
1
@ b-liu Lo he visto usado por desarrolladores experimentados en código nuevo. También puede ser útil cuando se usa define_method: apidock.com/ruby/Module/define_method .
Mike Vallano
¡Increíble! ¡Montones de gracias! @MikeVallano
B Liu
4

¿Qué hace enviar?

send es otra forma de llamar a un método.

Esto se ilustra mejor con un ejemplo:

o = Object.new
o.send(:to_s) # => "#<Object:0x00005614d7a24fa3>"
# is equivalent to:
o.to_s # => "#<Object:0x00005614d7a24fa3>"

Envía vidas en la clase Objeto .

¿Cuál es el beneficio de esto?

El beneficio de este enfoque es que puede pasar el método que desea llamar como parámetro. He aquí un ejemplo sencillo:

def dynamically_call_a_method(name)
    o = Object.new
    o.send name 
end
dynamically_call_a_method(:to_s) # => "#<Object:0x00005614d7a24fa3>"

Puede pasar el método al que desea que se le llame. En este caso pasamos :to_s. Esto puede ser muy útil cuando hacemos metaprogramación de ruby, porque nos permite llamar a diferentes métodos de acuerdo con nuestros diferentes requisitos.

BKSpurgeon
fuente
0

Otro caso de uso para las vistas:

    <%= link_to 
    send("first_part_of_path_#{some_dynamic_parameters}_end_path", 
    attr1, attr2), ....
    %>

Permitir. que escriba una vista escalable que trabaje con todo tipo de objetos con:

    render 'your_view_path', object: "my_object"
JustAnotherRubyLover
fuente
Esto agrega lógica innecesaria a las vistas y podría tener implicaciones de seguridad. No hagas esto. Utilice matrices y hashes.
Derrek Bertrand