Что send () делает в Ruby?

96

Кто-нибудь, пожалуйста, скажите мне, что

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

есть и делает?

Кристиан Банкистер
источник
2
Это вся строка кода? Я имею в виду, нет ничего перед словом «отправить»?
giraff

Ответы:

107

send отправляет сообщение экземпляру объекта и его предкам в иерархии классов до тех пор, пока какой-либо метод не отреагирует (поскольку его имя совпадает с первым аргументом).

На практике эти строки эквивалентны:

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

Обратите внимание, что sendпроверка видимости обходится, поэтому вы также можете вызывать частные методы (полезно для модульного тестирования).


Если перед отправкой действительно нет переменной, это означает, что используется глобальный объект:

send :to_s    # "main"
send :class   # Object
жираф
источник
1
О, понятно, так что можно было бы использовать send, если бы кто-то хотел сохранить в базе данных что-то вроде 1.month вместо того, чтобы статически указывать количество дней.
Christian Bankester,
3
Правда, вы можете использовать его для вызова метода с вычисляемыми, а не статическими именами. (Вы не должны разрешать неограниченный ввод данных пользователем, чтобы избежать вызова частных методов ... Однако вы можете дать им уникальный префикс: send 'user_method _' + methodname, * args)
giraff
2
Хорошим вариантом использования может быть то, что вы хотите протестировать метод защищенного класса, вы можете вызвать его вне класса - в тестовом файле ..
GN.
108

send - это рубиновый (без рельсов) метод, позволяющий вызывать другой метод по имени.

Из документации

   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

Никита Рыбак
источник
5
Отличный ответ, более ясный, чем принятый многословный ответ.
aaron-coding
63

Я думаю, что одна из самых полезных функций метода .send заключается в том, что он может динамически вызывать метод. Это может сэкономить вам много времени на набор текста. Одно из самых популярных применений метода .send - динамическое присвоение атрибутов. Например:

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

Чтобы назначать атрибуты регулярно, необходимо:

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

Или используя метод .send:

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

Но все это можно заменить следующим:

Предполагая, что вашему приложению Rails необходимо назначить атрибуты вашему классу автомобиля из пользовательского ввода, вы можете сделать

c = Car.new()
params.each do |key, value|
  c.send("#{key}=", value)
end
Антонио Джа
источник
Спасибо за отличную ссылку
sid_09 07
7
Использование .send таким образом добавляет ненужной сложности и упрощает случайное внесение ошибки в код. Например, в приведенном выше коде, если вы добавите новую запись в хэш параметров (например, «цилиндры»), код завершится ошибкой с неопределенной ошибкой метода.
Кевин Швердтфегер
1
response_to? при желании можно использовать для предотвращения таких ошибок.
Richard_G
1
Это было отличное объяснение! Спасибо, Джа!
Sharath
1
@Kevin, ты прав, но иногда это может быть необходимо. Большая гибкость коррелирует с большим риском, который можно снизить.
Уилл Шеппард
12

Другой пример, похожий на https://stackoverflow.com/a/26193804/1897857 Антонио Джа

если вам нужно прочитать атрибуты объекта.

Например, если у вас есть массив строк, если вы попытаетесь перебрать их и вызвать их на своем объекте, это не сработает.

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

Однако вы можете sendстроки к объекту:

atts = ['name', 'description']
@project = Project.first
atts.each do |a|
  puts @project.send(a)
end
# => Vandalay Project
# => A very important project
Майк Валлано
источник
Спасибо за простое и легкое объяснение!
Джунан Чакма
Спасибо! Это именно тот ответ, который мне нужен. Хотите знать, часто ли это используется? Я наткнулся на нечто похожее в устаревшем коде, не уверен, что мне стоит его придерживаться. @ Mike Vallano
B Liu
1
@ b-liu Я видел, как опытные разработчики использовали его в новом коде. Это также может быть полезно при использовании define_method: apidock.com/ruby/Module/define_method .
Майк Валлано
Потрясающие! Большое спасибо! @MikeVallano
B Liu
4

Что отправляет?

send это еще один способ вызова метода.

Лучше всего это проиллюстрировать на примере:

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

Отправить жизни в классе Object .

Какая польза от этого?

Преимущество этого подхода в том, что вы можете передать метод, который хотите вызвать, в качестве параметра. Вот простой пример:

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

Вы можете передать метод, который хотите вызвать. В данном случае мы прошли :to_s. Это может быть очень удобно при выполнении метапрограммирования Ruby, потому что это позволяет нам вызывать разные методы в соответствии с нашими различными требованиями.

BKSpurgeon
источник
0

Другой вариант использования представлений:

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

Позволять . вы должны написать масштабируемое представление, которое работает со всеми видами объектов с:

    render 'your_view_path', object: "my_object"
JustAnotherRubyLover
источник
Это добавляет ненужную логику к представлениям и может иметь последствия для безопасности. Не делай этого. Используйте массивы и хеши.
Деррек Бертран