модуль 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
Представление #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 для грубой модульности «это внутренняя часть».
Методы публичного экземпляра
# File activesupport/lib/active_support/core_ext/module/concerning.rb, line 126
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 109 def concerning(topic, &block) include concern(topic, &block) end
Определить новую задачу и смешать её.
© 2004–2018 David Heinemeier Hansson
Licensed under the MIT License.