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