Rails:如何从包含的关注中覆盖或私有化给定范围?

Rails: How to override or make private a given scope from an included Concern?

我担心在包含它的 ActiveRecord class 中添加 scope。大多数时候它的方式很好,但取决于 class 可能需要满足额外的标准。我不想重写模型中的范围,而是能够添加一个额外的标准方法。

这是我正在尝试做的事情:

module MyConcern
  extend ActiveSupport::Concern

  included do
    # I've included the fact I pass a limit variable here because my scope also does this, in case that's relevant for solving this.
    # I'd like this to be private or not accessible via `MyModel._my_scope`
    scope :_my_scope, ->(limit = nil) { (joins, wheres, order etc etc).limit(limit) }
    scope :my_scope, ->(limit = nil) { _my_scope(limit) }
  end
end

class MyModel < ApplicationRecord
  include MyConcern

  # Including the "private" scope to add my extra criteria.
  scope :my_scope, ->(limit = nil) { _my_scope(limit).where(foo: 'bar') }
end

class AnotherModel < ApplicationRecord
  include MyConcern

  # I like the default behavior here so nothing else to do
end

这行得通,但是在 class 之外,您可以这样做:MyModel._my_scope 我想这没问题 - 也许有时我想要默认行为 - 但在这种情况下我不认为我这样做,我觉得封装 _my_scope 是要走的路。

我假设可以在 MyConcern 中创建一个私有的 class 方法,它包含在 MyModel 中,但这似乎不起作用,我也不是真的Concern/mixin master 还不确定如何去做。另外, Concerns 是否被认为是混入?有区别吗?也很高兴知道。

您可以使用 class 方法实现范围的相同功能,您可以为这种情况继承和扩展这些功能。它与您的实施没有太大区别;只是通过使用 class 方法而不是范围来避免使用额外的 _ 方法。例如

module MyConcern
  extend ActiveSupport::Concern

  class_methods do
    def my_scope(limit = nil)
      (joins, wheres, order etc etc).limit(limit)
    end
  end
end

class MyModel < ApplicationRecord
  include MyConcern

  def self.my_scope(limit = nil)
    super.where(foo: 'bar')
  end
end

class AnotherModel < ApplicationRecord
  include MyConcern
end

对于问题的第二部分:从技术上讲,问题是 ruby Mixin;这只是 organize/group 仅作为关注点包含在您的模型中的 Mixins 的约定。使用 ActiveSupport::Concern 允许您向 Mixins 添加其他与模型相关的功能,例如范围、验证等,这是您无法使用普通模块获得的。 例如。你做不到

module MyConcern
  scope :my_scope, ->(limit = nil) { _my_scope(limit) }
end

class MyModel < ApplicationRecord
  include MyConcern # this will raise an error
end