2010-07-26 23 views

Respuesta

65

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

la práctica, esas líneas son equivalentes:

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

Tenga en cuenta que no pasa por send comprobaciones de visibilidad, por lo que se puede llamar a los métodos privados, también (útiles para las pruebas unitarias).


Si realmente no hay variable antes de enviar, que significa que se utiliza el objeto global:

send :to_s # "main" 
send :class # Object 
+0

Oh veo, así que uno podría usar enviar si uno quisiera almacenar algo así como 1.meses en la base de datos en lugar de decir estáticamente el número de días. –

+2

Cierto, podría usarlo para llamar al método con nombres que son calculados, no estáticos. (No debe permitir la entrada sin restricciones del usuario, sin embargo, para evitar llamar a métodos privados ... Podría, sin embargo, darles un prefijo único: send 'user_method _' + methodname, * args) – giraff

+0

Buen caso de uso podría ser el que desee para probar un método de clase protegido, puede llamarlo fuera de un archivo de prueba de clase ... –

69

enviar es un método de rubí (sin rieles) que permite invocar a 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

+3

Gran respuesta, más clara que la respuesta verbosa aceptada. –

33

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

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

Para asignar atributos regularmente una necesitaría

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

O usando .send método:

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

Pero todo puede ser sustituido por el siguiente:

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

c = Car.new() 
params.each do |key, value| 
    c.send("#{key}=", value) 
end 
+0

Gracias por el excelente enlace –

+3

Usar .send de esta manera agrega complejidad innecesaria y hace que sea más fácil introducir inadvertidamente un error en el código. Por ejemplo, en su código anterior, si agrega una nueva entrada a su parámetro hash (como 'cilindros'), el código fallará con un error de método indefinido. –

+1

respon_to? podría usarse para evitar tales errores, si así lo desea. –

8

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

es si usted necesita leer los atributos de un objeto.

Por ejemplo, si tiene una matriz de cadenas, si intenta recorrerlas y llamarlas a 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 send las cuerdas al objeto:

atts = ['name', 'description'] 
@project = Project.first 
atts.each do |a| 
    puts @project.send(a) 
end 
# => Vandalay Project 
# => A very important project 
+0

¡Gracias por la explicación simple y fácil! –

+0

¡Gracias! Esa es exactamente la respuesta que estoy buscando. Preguntarse es esto comúnmente usado? Me encontré con algo similar en el código heredado, no estoy seguro de que me quede con él. @ Mike Vallano –

+0

@ b-liu Lo he visto utilizado por desarrolladores experimentados en el nuevo código. También puede ser útil al usar 'define_method': https://apidock.com/ruby/Module/define_method. –

0

Otro caso de uso de vistas:

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

Permitir. usted debe escribir vista escalable que trabaje con todo tipo de objetos con:

render 'your_view_path', object: "my_object"