什么是覆盖ActiveRecord的模型和集合查找最干净的方法是什么?

时间:2008-11-24 17:04:28

标签: ruby-on-rails ruby activerecord finder

我的库代码覆盖了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扩展的简洁约定,而不会影响应用程序中的所有模型。

3 个答案:

答案 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'方法。