跳至內容 跳至搜尋

Active Support 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 呢?我們可以試著直接在 Bar 中包含 Foo 來向 Host 隱藏這些相依性

module Bar
  include Foo
  def self.included(base)
    base.method_injected_by_foo
  end
end

class Host
  include Bar
end

很不幸地,這無法運作,因為當包含 Foo 時,它的 baseBar 模組,而不是 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

前置 concerns

就像 include 一樣,concerns 也支援 prepend,並有一個對應的 prepended do 回呼。module ClassMethodsclass_methods do 也會前置。

prepend 也用於任何相依性。

方法
C
I
P

實例公開方法

class_methods(&class_methods_module_definition)

從給定的區塊定義類別方法。您也可以定義私有類別方法。

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 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

included(base = nil, &block)

在基底類別的內容中評估給定的區塊,以便您可以在此處撰寫類別巨集。當您定義多個included區塊時,它會引發例外。

# 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

prepended(base = nil, &block)

在基底類別的內容中評估給定的區塊,以便您可以在此處撰寫類別巨集。當您定義多個prepended區塊時,它會引發例外。

# 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