Що робить send () у Ruby?


95

Хтось може сказати мені що

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

є і робить?


2
Це весь рядок коду? Я маю на увазі, чи немає нічого перед "відправити"?
жираф

Відповіді:


107

send надсилає повідомлення екземпляру об'єкта та його предкам в ієрархії класів, поки якийсь метод не зреагує (оскільки його ім'я відповідає першому аргументу).

Практично кажучи, ці рядки еквівалентні:

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

Зверніть увагу, що sendобходить перевірку видимості, щоб ви могли також викликати приватні методи (корисно для модульного тестування).


Якщо перед відправленням дійсно немає змінної, це означає, що використовується глобальний об'єкт:

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

1
О, я розумію, тому можна використовувати send, якщо хочете зберегти щось на зразок 1.month у базі даних, а не статично вказувати кількість днів.
Крістіан Банкестер

3
Правда, ви можете використовувати його для виклику методу з обчисленими іменами, а не статичними. (Однак ви не повинні дозволяти необмежений ввід користувача, щоб уникнути виклику приватних методів ... Однак ви можете дати їм унікальний префікс: надіслати 'user_method _' + ім'я методу, * args)
жираф

2
Хорошим варіантом використання може бути те, що ви хочете протестувати захищений метод класу, ви можете викликати його поза класом - у тестовому файлі ..
GN.

107

надіслати - це рубіновий (без рейок) метод, що дозволяє викликати інший метод за іменем.

З документації

   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

7
Використання .send таким чином додає зайвої складності та полегшує ненавмисне введення помилки в код. Наприклад, у наведеному вище коді, якщо ви додасте новий запис до хешу параметрів (наприклад, „циліндри“), код не вдасться з невизначеною помилкою методу.
Кевін Швердтфегер

1
відповідати на? за потреби можна використовувати для запобігання таким помилкам.
Richard_G

1
Це було чудове пояснення! Дякую Джа!
Шарат,

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 .
Mike Vallano

Дивовижно! Спасибі купи! @MikeVallano
B Liu

4

Що робить send?

send є ще одним способом виклику методу.

Це найкраще проілюстровано на прикладі:

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

Надсилайте життя в клас Object .

Яка користь від THS?

Перевага цього підходу полягає в тому, що ви можете передати метод, який ви хочете викликати як параметр. Ось простий приклад:

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.