为什么ActiveRecord :: Base上的猴子修补方法重新定义了Model.count上不相关的方法

时间:2016-09-12 18:57:21

标签: ruby-on-rails activerecord monkeypatching

当我使用类方法修补ActiveRecord :: Base时,这些方法由不同的模型ActiveRecord_Relation类(如User :: ActiveRecord_Relation)继承,并且可以在特定活动记录关系的实例上调用。但是,在为原始模型进行活动记录调用时,这会导致一些意外行为。

这是一个微不足道的例子:

User.count
=> 3544

users = User.where("created_at > ?", 1.month.ago)
users.count
=> 174

class ActiveRecord::Base
    def self.monkey_foo(options = {}, &block)
        User.count
    end
end

User.monkey_foo
=> 3544

Book.monkey_foo # Another model
=> 3544

users.monkey_foo
=> 173 # This is the count of the users relation, not the User model

Book.where("created_at > 1.year.ago").monkey_foo
=> 3544 # Issue only affects User model relations

导致此行为的原因是什么?

我知道像这样的猴子修补对于任何严肃的事情都是一个非常糟糕的主意。我不小心发现了这种行为,我非常好奇地知道它为什么会发生。

1 个答案:

答案 0 :(得分:2)

这个问题的关键在delegation.rb

基本上,这种方法缺少关于Relation的实现(为了简洁而略微简化)

def method_missing(method,*args,&block)
  scoping { @klass.public_send(method, *args, &block) }
end

(@ klass是该关系所属的活动记录类)

范围方法设定班级'阻止持续时间current_scope。这包含where子句,排序等等。这使得您可以在关系上调用类方法,并让这些类方法在关系定义的范围内运行。

在本书案例中,这种情况仍然存在,但是确定范围发生在Book,但查询是针对用户的,因此范围不会改变结果。