- PVSM.RU - https://www.pvsm.ru -
Один из методов [1] библиотеки Sidekiq. Объяснение смайла [2]
send в Ruby вызывает методы объектов по имени. Вот очевидный способ применения:
# До: явно используем присваивание. Неудобно, если полей много или они определяются в рантайме.
user.name = "Иван"
user.age = 29
# После: передаём имя атрибута параметром. Решает проблемы первого способа.
def set(field, value)
send("#{field}=", value)
end
user.set(:name, "Иван")
user.set(:age, 29)
А ещё вы наверняка видели такие строки:
after_create :send_email
Да-да, коллбэки в рельсах внутри реализованы тоже с помощью send [3].
Ещё через send при тестировании вызывают приватные методы. О том, нужно ли вообще их тестировать, рассказывает [4] Sandi Metz, автор книги Practical Object-Oriented Design in Ruby [5] (о приватных методах с 10:58).
Она считает тестирование приватных методов излишним: правильный набор входных данных при тестировании публичных методов обеспечит 100%-е покрытие. Но периодически при активной разработке нестабильного кода Sandi нарушает это правило, чтобы не копаться в трейсах, а ловить ошибку в месте возникновения. Такие тесты она считает временными и с готовностью удаляет, когда код стабилизируется. Она в курсе про подход "Если вам нужно тестировать приватный метод, вынесите его в отдельный класс", но считает, что от такого выделения код не станет более стабильным.
Иногда без send не обойтись:
data_point = OpenStruct.new(:queued? => true)
data_point.queued? # -> true
data_point.send("queued?=",false) # иначе такой метод не вызвать
data_point.queued? # -> false
Это пример из документации по OpenStruct. Пример надуманный, но встречаются названия и более странные: взять хоть тот же метод из картинки в начале поста.
Часто пытаетесь выйти из irb командой учше? Просто добавьте в ~/.irbrc
следующий код:
module Kernel
def учше
exit
end
end
Пришло в голову, пока обдумывал примеры для статьи; добавил ради теста себе в .irbrc — работает. Собирался было удалить, потом думаю: "А ведь удобно же". Пока оставил, пускай полежит недельку.
module Kernel
def method_missing(method_name, *arguments, &block)
return(super) unless contains_russian_letters?(method_name.to_s)
possible_meaning = translit(method_name.to_s)
send(possible_meaning, *arguments)
end
private
def translit(string)
string.chars.map do |char|
russian_to_english_mapping[char] || char
end.join
end
def contains_russian_letters?(string)
!(string.chars & russian_symbols).empty?
end
def russian_symbols
"йцукенгшщзхъфывапролджэячсмитьбю".chars
end
def russian_to_english_mapping
english = "qwertyuiop[]asdfghjkl;'zxcvbnm,.".chars
russian_symbols.zip(english).to_h
end
end
# Пробуем:
puts(1.inspect) # Выводит 1
згеы(1.штызусе) # Тоже выводит. Работает!
Оставлять это не решился.
Неаккуратное использование send ухудшает читаемость. Сравните:
# Вариант 1. Читаемо, но не масштабируемо
if params[:sort_by] == "age"
users.sort_by_age
else
users.sort_by_name
end
# Вариант 2. Масштабируемо, но нечитаемо (в примере параметры не фильтруются)
sorting_method = "sort_by_#{params[:sort_by]}"
users.send(sorting_method)
# Лучше переписать код так, чтобы можно было писать
# и масштабируемо, и читаемо:
users.sort_by(params[:sort_by])
Используйте send, если вам нужно:
Не используйте send без необходимости.
Автор: HedgeSky
Источник [6]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/ruby/226503
Ссылки в тексте:
[1] Один из методов: https://github.com/mperham/sidekiq/blob/b63a8571524e1e505c93c97c2bdb1c352f845064/lib/sidekiq.rb#L51
[2] Объяснение смайла: http://stackoverflow.com/questions/23975346/why-is-%E2%95%AF%C2%B0%E2%96%A1%C2%B0-%E2%95%AF-%E2%94%BB%E2%94%81%E2%94%BB-with-such-an-encoding-used-for-a-method-name
[3] тоже с помощью send: https://github.com/rails/rails/blob/38fe5ae24476ef8808d5eb6366afe84ff43a3279/activesupport/lib/active_support/callbacks.rb#L432
[4] рассказывает: https://vimeo.com/48106365
[5] Practical Object-Oriented Design in Ruby: http://www.poodr.com/
[6] Источник: https://habrahabr.ru/post/318546/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.