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

随机位

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

骨折

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

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章