覆盖 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 插件,所以我想要一个适用于模型和范围集合而不影响应用程序中的所有模型的可插入查找器扩展的干净约定。
I have library code that overrides Ar's find method. I also include the module for all Association classes so both MyModel.find and @parent.my_models.find work and apply the correct scope.
I based my code off of will_paginate's:
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
My problem is, I only want to override the finders for some models. Currently I need to extend all association collection classes which are shared by all models. I know I can extend associations per model by passing a module:
has_many :things, :extend => SomeCustomMethods
But my library is basically am ActiveRecord plugin, so I'd like a clean convention for plugable finder extensions that apply to both the model and scoped collections without affecting all models in the application.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您想要重写
find_every
,这是最终将使用相应查询运行find_by_sql
的 AR 方法。 重写find
对于自定义查找器不起作用,而且它更脆弱。但为了与其他插件兼容,你不能只重载这个方法。 相反,为其添加别名并在执行所需操作后调用原始实现:
这将为所有类启用您的插件。 您想如何控制启用哪些模型? 也许是一个标准的插件访问器?
为了支持这一点,您需要将
class << base
到类方法(因此base
应该是self
)。 喜欢:You want to override
find_every
, which is the AR method that will ultimately runfind_by_sql
with the corresponding query. Overridingfind
won't work for customized finders, and it's just more fragile.But to be compatible with other plugins you can't just overload this method. Instead, alias it and call the original implementation after doing what you want:
This will enable your plugin for all classes. How do you want to control which models are enabled? Maybe a standard plugin accessor?
To support this you need to move the
class << base
to a class method (thusbase
should beself
). Like:首先,确保你熟悉Ruby的方法调用继承结构 ,因为如果没有这个,你最终可能会在黑暗中四处乱刺。
在 ActiveRecord 类中执行此操作的最直接方法是:
这也适用于关联,因为它们本身使用基查找器。 现在您只需要进行定制即可。 其复杂性可能差异很大,我不知道你在做什么,所以我无法提供任何建议。
此外,动态定义它本身就是一项练习,但这应该能让您指明正确的方向。
First of all, make sure you know Ruby's method call inheritance structure well, as without this you can end up stabbing around in the dark.
The most straightforward way to do this inside an ActiveRecord class is:
That will apply to associations as well since they use the base finder themselves. Now you just need to do your customization. The complexity of that can vary widely, and I don't know what you're doing so I can't offer any recommendations.
Also defining this dynamically will be an exercise unto itself, but this should get you pointed in the right direction.
佩德罗的回答是正确的,但有一个小错误。
应该是
Using class << base ... end 具有在类方法范围内对“base”调用“extend”的效果,但 ActiveRecord::Base 中没有方法“base”,因此会引发错误。 单独使用base.extend将调用ActiveRecord::Base的'extend'方法。
'Pedro's answer is right, but there's a small mistake.
should be
Using class << base ... end has the effect of calling 'extend' on 'base' in the class-method scope, but there is no method 'base' in ActiveRecord::Base so an error is thrown. Using base.extend by itself will call the 'extend' method of ActiveRecord::Base.