我有库代码覆盖了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的方法调用继承结构,因为如果没有这个,你最终可能会在黑暗中刺伤.
在ActiveRecord类中执行此操作的最直接方法是:
def self.find(*args) super end
这也将适用于协会,因为他们自己使用基础查找器.现在您只需要进行自定义.其复杂程度可能有很大差异,我不知道你在做什么,所以我不能提出任何建议.
动态定义它本身也是一种练习,但这应该让你指向正确的方向.