3

我有覆盖 Ar 的 find 方法的库代码。我还包括所有 Association 类的模块,因此 MyModel.find 和 @parent.my_models.find 都可以工作并应用正确的范围。

我基于 will_paginate 的代码:

a = ActiveRecord::Associations
returning([ a::AssociationCollection ]) { |classes|
  # detect http://dev.rubyonrails.org/changeset/9230
  unless a::HasManyThroughAssociation.superclass == a::HasManyAssociation
    classes << a::HasManyThroughAssociation
  end
}.each do |klass|
  klass.send :include, Finder::ClassMethods
  klass.class_eval { alias_method_chain :method_missing, :paginate }
end

我的问题是,我只想覆盖某些模型的查找器。目前我需要扩展所有模型共享的所有关联集合类。我知道我可以通过传递一个模块来扩展每个模型的关联:

has_many :things, :extend => SomeCustomMethods

但是我的库基本上是 ActiveRecord 插件,所以我想要一个适用于模型和范围集合的可插入查找器扩展的干净约定,而不影响应用程序中的所有模型。

4

3 回答 3

10

您想要覆盖find_every,这是最终将find_by_sql与相应查询一起运行的 AR 方法。覆盖find不适用于自定义查找器,而且它更脆弱。

但是要与其他插件兼容,您不能只重载此方法。取而代之的是,在做你想做的事情后给它取别名并调用原始实现:

module MyPlugin
  def self.included(base)
    class << base
      alias_method :find_every_without_my_plugin, :find_every
      def find_every(*args)
        # do whatever you need ...
        find_every_without_my_plugin(*args)
      end
    end
  end
end

ActiveRecord::Base.send :include, MyPlugin

这将为所有类启用您的插件。您想如何控制启用哪些模型?也许是标准插件访问器?

class User < ActiveRecord::Base
  my_plugin
end

为了支持这一点,您需要将 移动class << base到类方法(base应该是self)。喜欢:

module MyPlugin
  def self.included(base)
    class << base
      base.extend ClassMethods
    end
  end

  module ClassMethods
    def my_plugin
      class << self
        alias_method :find_every_without_my_plugin, :find_every
        # ...
      end
    end
  end
end
于 2008-11-25T05:03:59.377 回答
6

首先,确保你对 Ruby 的方法调用继承结构了如指掌,否则你最终可能会在黑暗中摸索。

在 ActiveRecord 类中执行此操作的最直接方法是:

def self.find(*args)
  super
end

这也适用于关联,因为它们自己使用基础查找器。现在您只需要进行自定义。其复杂性可能相差很大,我不知道您在做什么,所以我无法提供任何建议。

同样动态地定义它本身就是一个练习,但这应该会让你指向正确的方向。

于 2008-11-24T19:05:31.473 回答
0

'佩德罗的回答是对的,但有一个小错误。

def self.included(base)
  class << base
    base.extend ClassMethods
  end
end

应该

def self.included(base)
  base.extend ClassMethods
end

使用 class << base ... end 具有在类方法范围内对 'base' 调用 'extend' 的效果,但 ActiveRecord::Base 中没有方法 'base',因此会引发错误。单独使用 base.extend 将调用 ActiveRecord::Base 的 'extend' 方法。

于 2010-01-15T01:31:02.307 回答