ruby-on-rails 为什么使用ActiveSupport::Concern而不是简单的模块?

xuo3flqw  于 2023-10-21  发布在  Ruby
关注(0)|答案(2)|浏览(119)

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

cigdeys3

cigdeys31#

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呢?我们可以尝试直接将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
nr9pn0ug

nr9pn0ug2#

使用ActiveSupport::关注常规mixin的一个原因是它延迟加载包含。因此,如果您引用模型、控制器等中的代码,它将解析。然而,当加载mixin时,您可能会得到未定义的错误。

相关问题