модуль Module::Concerning
Разделение задач небольшими блоками
Часто мы сталкиваемся с блоком поведения среднего размера, который хотели бы выделить, но при этом использовать только в одном классе.
Часто хорошим выбором является создание обычного объекта Ruby для его инкапсуляции и сотрудничества или делегирования исходному объекту, но когда нет дополнительного состояния для инкапсуляции или мы делаем объявления в стиле DSL для родительского класса, введение новых коллабораторов может запутать, а не упростить.
Обычно всё помещают в монолитный класс, возможно, с комментарием, как наименее плохой вариант. Использование модулей в отдельных файлах означает утомительное просеивание, чтобы получить общее представление.
Неудовлетворительные способы разделения небольших задач
Использование комментариев:
class Todo < ApplicationRecord
# Other todo implementation
# ...
## Event tracking
has_many :events
before_create :track_creation
private
def track_creation
# ...
end
end
С встроенным модулем:
Шумный синтаксис.
class Todo < ApplicationRecord
# Other todo implementation
# ...
module EventTracking
extend ActiveSupport::Concern
included do
has_many :events
before_create :track_creation
end
private
def track_creation
# ...
end
end
include EventTracking
end
Вставка шума в отдельный файл:
Как только наш блок поведения начинает выходить за пределы области видимости, мы сдаёмся и переносим его в отдельный файл. В таком случае увеличенные накладные расходы могут стать разумным компромиссом, даже если это уменьшит наше представление о работе.
class Todo < ApplicationRecord # Other todo implementation # ... include TodoEventTracking end
Введение Module#concerning
Утишая шум, мы получаем естественный и простой способ разделения мелких задач.
class Todo < ApplicationRecord
# Other todo implementation
# ...
concerning :EventTracking do
included do
has_many :events
before_create :track_creation
end
private
def track_creation
# ...
end
end
end
Todo.ancestors
# => [Todo, Todo::EventTracking, ApplicationRecord, Object]
Этот небольшой шаг имеет ряд замечательных последствий. Мы можем
-
понять поведение нашего класса с первого взгляда,
-
очистить монолитные классы с обширным функционалом, разделив их задачи, и
-
перестать полагаться на protected/private для грубой модульности «это внутренние данные».
Префикс concerning
concerning поддерживает аргумент prepend: true, который prepend задачу вместо использования include.
Открытые методы экземпляра
# File activesupport/lib/active_support/core_ext/module/concerning.rb, line 132
def concern(topic, &module_definition)
const_set topic, Module.new {
extend ::ActiveSupport::Concern
module_eval(&module_definition)
}
end Краткая запись для определения задачи.
concern :EventTracking do ... end
эквивалентно
module EventTracking extend ActiveSupport::Concern ... end
# File activesupport/lib/active_support/core_ext/module/concerning.rb, line 114 def concerning(topic, prepend: false, &block) method = prepend ? :prepend : :include __send__(method, concern(topic, &block)) end
Определение новой задачи и её добавление.
© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.