С https://api.rubyonrails.org/classes/ActiveSupport/Concern.html:
Типичный модуль выглядит следующим образом:
module M
def self.included(base)
base.extend ClassMethods
base.class_eval do
scope :disabled, -> { where(disabled: true) }
end
end
module ClassMethods
...
end
end
Используя ActiveSupport::Concern
, вышеприведенный модуль может быть записан как:
require 'active_support/concern'
module M
extend ActiveSupport::Concern
included do
scope :disabled, -> { where(disabled: true) }
end
class_methods do
...
end
end
Более того, он изящно обрабатывает зависимости модуля.Учитывая модуль Foo
и модуль Bar
, который зависит от первого, мы обычно пишем следующее:
module Foo
def self.included(base)
base.class_eval do
def self.method_injected_by_foo
...
end
end
end
end
module Bar
def self.included(base)
base.method_injected_by_foo
end
end
class Host
include Foo # We need to include this dependency for Bar
include Bar # Bar is the module that Host really needs
end
Но почему Host
следует заботиться о зависимостях Bar
, а именноFoo
?Мы могли бы попытаться скрыть их от Host
напрямую, включая Foo
в Bar
:
module Bar
include Foo
def self.included(base)
base.method_injected_by_foo
end
end
class Host
include Bar
end
К сожалению, это не сработает, поскольку при включении Foo
его основой является Bar
модуль, а не Host
класс.С ActiveSupport::Concern
зависимости модуля правильно разрешены:
require 'active_support/concern'
module Foo
extend ActiveSupport::Concern
included do
def self.method_injected_by_foo
...
end
end
end
module Bar
extend ActiveSupport::Concern
include Foo
included do
self.method_injected_by_foo
end
end
class Host
include Bar # It works, now Bar takes care of its dependencies
end