Что 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, потому что это позволяет нам вызывать разные методы в соответствии с нашими различными требованиями.


0

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

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

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

    render 'your_view_path', object: "my_object"

Это добавляет ненужную логику к представлениям и может иметь последствия для безопасности. Не делай этого. Используйте массивы и хеши.
Деррек Бертран
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.