Spec-Zone.ru › Ruby on Rails 5.2

модуль 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

Методы публичного экземпляра

append_features(base) Показать исходный код
# File activesupport/lib/active_support/concern.rb, line 113
def append_features(base)
  if base.instance_variable_defined?(:@_dependencies)
    base.instance_variable_get(:@_dependencies) << self
    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
Вызывает метод суперкласса
class_methods(&class_methods_module_definition) Показать исходный код
# File activesupport/lib/active_support/concern.rb, line 136
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
included(base = nil, &block) Показать исходный код
# File activesupport/lib/active_support/concern.rb, line 126
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.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API