【问题标题】:What's the cleanest way to override ActiveRecord's find for both models and collections?覆盖 ActiveRecord 对模型和集合的查找的最干净的方法是什么?
【发布时间】:2010-09-23 19:23:36
【问题描述】:

我有重写 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 插件,所以我想要一个干净的可插入查找器扩展约定,适用于模型和作用域集合,而不影响应用程序中的所有模型。

【问题讨论】:

    标签: ruby-on-rails ruby activerecord finder


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      首先,请确保您对 Ruby 的 method call inheritance structure 了如指掌,否则您可能会在黑暗中四处乱窜。

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

      def self.find(*args)
        super
      end
      

      这也适用于关联,因为它们自己使用基础查找器。现在您只需要进行自定义。其复杂性可能差异很大,我不知道您在做什么,因此无法提供任何建议。

      同时动态定义它本身就是一个练习,但这应该会让你找到正确的方向。

      【讨论】:

      • 这不适用于构建查询。 MyModel.where(some: 'thing').find 不使用 MyModel.find
      【解决方案3】:

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

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

      应该是

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

      使用 class

      【讨论】:

        猜你喜欢
        • 2019-12-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多