Cosa fa send () in Ruby?


95

Qualcuno può dirmi cosa

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

è e fa?


2
Questa è l'intera riga di codice? Voglio dire, non c'è niente prima di "inviare"?
giraff

Risposte:


107

send invia un messaggio a un'istanza di un oggetto e ai suoi antenati nella gerarchia di classi finché un metodo non reagisce (perché il suo nome corrisponde al primo argomento).

In pratica, quelle righe sono equivalenti:

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

Si noti che sendignora i controlli di visibilità, in modo da poter chiamare anche metodi privati ​​(utile per i test di unità).


Se non c'è davvero nessuna variabile prima dell'invio, significa che viene utilizzato l'Oggetto globale:

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

1
Oh capisco, quindi si potrebbe usare send se si volesse memorizzare qualcosa come 1 mese sul database invece di dire staticamente il numero di giorni.
Christian Bankester

3
È vero, potresti usarlo per chiamare un metodo con nomi calcolati, non statici. (Non dovresti consentire l'input dell'utente senza restrizioni, però, per evitare di chiamare metodi privati ​​... Puoi, tuttavia, dare loro un prefisso univoco: send 'user_method _' + methodname, * args)
giraff

2
Un buon caso d'uso potrebbe essere quello di voler testare un metodo di classe protetto, potresti chiamarlo fuori da una classe - nel file di test ..
GN.


63

Una delle funzionalità più utili che penso con il metodo .send è che può chiamare dinamicamente il metodo. Questo può farti risparmiare un sacco di digitazione. Uno degli usi più diffusi del metodo .send è l'assegnazione dinamica degli attributi. Per esempio:

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

Per assegnare gli attributi regolarmente sarebbe necessario

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

O utilizzando il metodo .send:

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

Ma tutto può essere sostituito con il seguente:

Supponendo che la tua app Rails debba assegnare attributi alla tua classe di auto dall'input dell'utente, puoi farlo

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

Grazie per l'ottimo link
sid_09

7
L'uso di .send in questo modo aggiunge complessità non necessaria e rende più facile introdurre inavvertitamente un bug nel codice. Ad esempio, nel codice precedente, se aggiungi una nuova voce all'hash dei parametri (come "cilindri"), il codice fallirà con un errore di metodo non definito.
Kevin Schwerdtfeger

1
rispondere a? potrebbe essere utilizzato per prevenire tali errori, se lo si desidera.
Richard_G

1
Questa è stata un'ottima spiegazione! Grazie Jha!
Sharath

1
@Kevin hai ragione, ma a volte può essere necessario. Una maggiore flessibilità è correlata a un rischio maggiore, che può essere mitigato.
Will Sheppard

12

Un altro esempio, simile a https://stackoverflow.com/a/26193804/1897857 di Antonio Jha

è se hai bisogno di leggere gli attributi su un oggetto.

Ad esempio, se hai un array di stringhe, se provi a iterarle e chiamarle sul tuo oggetto, non funzionerà.

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

Tuttavia, puoi inserire sendle stringhe nell'oggetto:

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

Grazie per la semplice e facile spiegazione!
Junan Chakma

Grazie! Questa è esattamente la risposta che cerco. Ti stai chiedendo che sia comunemente usato? Mi sono imbattuto in qualcosa di simile nel codice legacy, non sono sicuro di doverlo mantenere. @ Mike Vallano
B Liu

1
@ b-liu L'ho visto usato da sviluppatori esperti nel nuovo codice. Può anche essere utile quando si utilizza define_method: apidock.com/ruby/Module/define_method .
Mike Vallano

Eccezionale! Grazie mille! @MikeVallano
B Liu

4

Cosa fa send?

send è un altro modo per chiamare un metodo.

Questo è meglio illustrato da un esempio:

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

Invia vite nella classe Object .

Qual è il vantaggio di questo?

Il vantaggio di questo approccio è che puoi passare il metodo che desideri chiamare come parametro. Qui c'è un semplice esempio:

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

Puoi passare il metodo che vuoi essere chiamato. In questo caso siamo passati :to_s. Questo può essere molto utile quando si esegue la metaprogrammazione di ruby, perché questo ci consente di chiamare diversi metodi in base alle nostre diverse esigenze.


0

Un altro caso d'uso per le visualizzazioni:

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

Permettere . di scrivere viste scalabili che lavorano con tutti i tipi di oggetti con:

    render 'your_view_path', object: "my_object"

Ciò aggiunge logica non necessaria alle viste e potrebbe avere implicazioni sulla sicurezza. Non farlo. Usa array e hash.
Derrek Bertrand
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.