модуль ActiveSupport::Concern
Типичный модуль выглядит так:
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, его base является модулем 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 Публичные методы экземпляров
# File activesupport/lib/active_support/concern.rb, line 111
def append_features(base)
if base.instance_variable_defined?(:@_dependencies)
base.instance_variable_get(:@_dependencies) << self
return false
else
return false if base < self
@_dependencies.each { |dep| base.include(dep) }
super
base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
end
end Вызывает метод суперкласса
# File activesupport/lib/active_support/concern.rb, line 134
def class_methods(&class_methods_module_definition)
mod = const_defined?(:ClassMethods, false) ?
const_get(:ClassMethods) :
const_set(:ClassMethods, Module.new)
mod.module_eval(&class_methods_module_definition)
end # File activesupport/lib/active_support/concern.rb, line 124
def included(base = nil, &block)
if base.nil?
raise MultipleIncludedBlocks if instance_variable_defined?(:@_included_block)
@_included_block = block
else
super
end
end Вызывает метод суперкласса
© 2004–2018 David Heinemeier Hansson
Licensed under the MIT License.