Que fait send () dans Ruby?


95

Quelqu'un peut-il me dire quoi

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

est et fait?


2
S'agit-il de toute la ligne de code? Je veux dire, n'y a-t-il rien avant «envoyer»?
giraff

Réponses:


107

send envoie un message à une instance d'objet et à ses ancêtres dans la hiérarchie de classes jusqu'à ce qu'une méthode réagisse (car son nom correspond au premier argument).

En pratique, ces lignes sont équivalentes:

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

Notez que sendcontourne les contrôles de visibilité, de sorte que vous pouvez également appeler des méthodes privées (utile pour les tests unitaires).


S'il n'y a vraiment pas de variable avant l'envoi, cela signifie que l'Objet global est utilisé:

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

1
Oh je vois, donc on pourrait utiliser send si l'on voulait stocker quelque chose comme 1. mois sur la base de données au lieu de dire statiquement le nombre de jours.
Christian Bankester du

3
Certes, vous pouvez l'utiliser pour appeler une méthode avec des noms calculés et non statiques. (Vous ne devriez pas autoriser une entrée utilisateur illimitée, cependant, pour éviter d'appeler des méthodes privées ... Vous pouvez cependant leur donner un préfixe unique: envoyer 'user_method _' + methodname, * args)
giraff

2
Un bon cas d'utilisation pourrait être si vous voulez tester une méthode de classe protégée, vous pouvez l'appeler en dehors d'une classe - dans le fichier de test ..
GN.


63

L'une des fonctionnalités les plus utiles que je pense de la méthode .send est qu'elle peut appeler dynamiquement une méthode. Cela peut vous épargner beaucoup de saisie. L'une des utilisations les plus courantes de la méthode .send consiste à attribuer des attributs de manière dynamique. Par exemple:

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

Pour attribuer des attributs régulièrement, il faudrait

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

Ou en utilisant la méthode .send:

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

Mais tout peut être remplacé par ce qui suit:

En supposant que votre application Rails doit attribuer des attributs à votre classe de voiture à partir de l'entrée utilisateur, vous pouvez faire

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

Merci pour le bon lien
sid_09

7
Utiliser .send de cette manière ajoute une complexité inutile et facilite l'introduction accidentelle d'un bogue dans le code. Par exemple, dans votre code ci-dessus, si vous ajoutez une nouvelle entrée à votre hachage de paramètres (comme «cylindres»), le code échouera avec une erreur de méthode non définie.
Kevin Schwerdtfeger

1
répondre à? pourrait être utilisé pour éviter de telles erreurs, si désiré.
Richard_G

1
C'était une excellente explication! Merci Jha!
Sharath

1
@Kevin vous avez raison, mais parfois cela peut être nécessaire. Plus de flexibilité correspond à plus de risques, qui peuvent être atténués.
Will Sheppard

12

Un autre exemple, similaire à https://stackoverflow.com/a/26193804/1897857 d' Antonio Jha

est si vous avez besoin de lire des attributs sur un objet.

Par exemple, si vous avez un tableau de chaînes, si vous essayez de les parcourir et de les appeler sur votre objet, cela ne fonctionnera pas.

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

Cependant, vous pouvez sendles chaînes de l'objet:

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

Merci pour une explication simple et facile!
Junan Chakma le

Merci! C'est exactement la réponse que je recherche. Vous vous demandez est-ce couramment utilisé? Je suis tombé sur quelque chose de similaire dans l'ancien code, je ne suis pas sûr de devoir m'en tenir à lui. @ Mike Vallano
B Liu

1
@ b-liu Je l'ai vu utilisé par des développeurs expérimentés dans un nouveau code. Cela peut également être utile lors de l'utilisation de define_method: apidock.com/ruby/Module/define_method .
Mike Vallano

Impressionnant! Merci des tas! @MikeVallano
B Liu

4

Que fait envoyer?

send est une autre façon d'appeler une méthode.

Ceci est mieux illustré par l'exemple:

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

Envoyez des vies dans la classe Object .

Quel est l'avantage de ths?

L'avantage de cette approche est que vous pouvez transmettre la méthode que vous souhaitez appeler en tant que paramètre. Voici un exemple simple:

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

Vous pouvez transmettre la méthode que vous souhaitez appeler. Dans ce cas, nous sommes passés :to_s. Cela peut être très pratique lors de la métaprogrammation ruby, car cela nous permet d'appeler différentes méthodes en fonction de nos différentes exigences.


0

Un autre cas d'utilisation des vues:

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

Autoriser . vous d'écrire des vues évolutives qui fonctionnent avec tous les types d'objets avec:

    render 'your_view_path', object: "my_object"

Cela ajoute une logique inutile aux vues et pourrait avoir des implications sur la sécurité. Ne fais pas ça. Utilisez des tableaux et des hachages.
Derrek Bertrand
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.