我的库代码覆盖了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扩展的简洁约定,而不会影响应用程序中的所有模型。
答案 0 :(得分:8)
您要覆盖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
答案 1 :(得分:5)
首先,确保你知道Ruby的method call inheritance structure,如果没有这个,你最终可能会在黑暗中刺伤。
在ActiveRecord类中执行此操作的最直接方法是:
def self.find(*args)
super
end
这也将适用于协会,因为他们自己使用基础查找器。现在您只需要进行自定义。其复杂程度可能有很大差异,我不知道你在做什么,所以我不能提出任何建议。
同样动态地定义它本身就是一种练习,但这应该让你指向正确的方向。
答案 2 :(得分:0)
'佩德罗的答案是对的,但是有一个小错误。
def self.included(base)
class << base
base.extend ClassMethods
end
end
应该是
def self.included(base)
base.extend ClassMethods
end
使用class&lt;&lt; base ... end具有在类方法范围内对'base'调用'extend'的效果,但ActiveRecord :: Base中没有方法'base',因此抛出错误。使用base.extend本身将调用ActiveRecord :: Base的'extend'方法。