对于模型和集合,覆盖ActiveRecord查找的最简洁方法是什么?

我有库代码覆盖了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插件,所以我想要一个适用于模型和范围集合的可插入finder扩展的简洁约定,而不会影响应用程序中的所有模型。

您想要覆盖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 

首先,确保你知道Ruby的方法调用inheritance结构 ,因为如果没有这个,你最终可能会在黑暗中刺伤。

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

 def self.find(*args) super end 

这也将适用于协会,因为他们自己使用基础查找器。 现在您只需要进行自定义。 其复杂程度可能差别很大,我不知道你在做什么,所以我不能提出任何建议。

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

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

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

应该

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

使用class << base ... end会在类方法范围内调用'base'的效果,但ActiveRecord :: Base中没有方法'base',因此会抛出错误。 使用base.extend本身将调用ActiveRecord :: Base的'extend'方法。