module ActiveSupport::Concern
Concern в Active Support
Типичный модуль выглядит так:
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 Добавление примесей в начало
Как и include, примеси также поддерживают prepend с соответствующим обратным вызовом prepended do. Кроме того, в начало добавляются module ClassMethods или class_methods do.
prepend также используется для любых зависимостей.
Общедоступные методы экземпляра
# File activesupport/lib/active_support/concern.rb, line 209
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 Определяет методы класса из указанного блока. Также можно определять закрытые методы класса.
module Example
extend ActiveSupport::Concern
class_methods do
def foo; puts 'foo'; end
private
def bar; puts 'bar'; end
end
end
class Buzz
include Example
end
Buzz.foo # => "foo"
Buzz.bar # => private method 'bar' called for Buzz:Class(NoMethodError)
# File activesupport/lib/active_support/concern.rb, line 158
def included(base = nil, &block)
if base.nil?
if instance_variable_defined?(:@_included_block)
if @_included_block.source_location != block.source_location
raise MultipleIncludedBlocks
end
else
@_included_block = block
end
else
super
end
end Выполняет указанный блок в контексте базового класса, чтобы здесь можно было записывать макросы класса. Если определить более одного блока included, будет вызвано исключение.
# File activesupport/lib/active_support/concern.rb, line 175
def prepended(base = nil, &block)
if base.nil?
if instance_variable_defined?(:@_prepended_block)
if @_prepended_block.source_location != block.source_location
raise MultiplePrependBlocks
end
else
@_prepended_block = block
end
else
super
end
end Выполняет указанный блок в контексте базового класса, чтобы здесь можно было записывать макросы класса. Если определить более одного блока prepended, будет вызвано исключение.
© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.