为什么要使用ActiveSupport :: Concern而不是仅使用普通模块?

时间:2018-11-26 20:26:11

标签: ruby-on-rails ruby-on-rails-5

我从来不明白为什么必须使用ActiveSupport :: Concern而不是普通模块来使用mixins。对于ActiveSupport :: Concern提供的功能(至少在Rails 5中),是否有一个简单的答案,即不使用ActiveSupport :: Concern的简单模块可以做什么?

1 个答案:

答案 0 :(得分:3)

来自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 ,可以正确解决模块依赖性:

{{1}}