¿Qué hace send () en Ruby?


94

¿Alguien puede decirme qué?

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

es y hace?


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

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.


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

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

¡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.


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"

Esto agrega lógica innecesaria a las vistas y podría tener implicaciones de seguridad. No hagas esto. Utilice matrices y hashes.
Derrek Bertrand
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.