2012-03-01 44 views
5

让我为你想象它。ActiveRecord如何检测链中的最后一个方法调用?

class Product < ActiveRecord::Base 
end 

Product.first.title 
#=> "My sample product" 

这里没什么特别的。只是一个简单的方法调用。现在看看下面的例子。

class Product < ActiveRecord::Base 
    def method_missing 
    end 
end 

Product.first.title 
#=> nil 

Product.first 
Product.first.title 
#=> "My sample product" 

这怎么可能?在某种程度上,他们决定了方法链的结束并对此采取行动?至少这是我的理论。

任何人都可以解释这种行为吗?

回答

7

您正在看到使用irb进行调查的工件。

当你这样说:

> Product.first.title 
#=> nil 

method_missing将被称为懒加载的title方法,你会得到nil

当你这样说:

> Product.first 

你有效地这样做:

> p = Product.first; puts p.inspect 

的第一款产品实例将被加载,然后irb将调用inspect它,AR将增加访问器方法。结果是产品现在有一个title方法。因此,这样做:

> Product.first 
> Product.first.title 

不会叫你method_missing都一样会有一个真正的title方法Product.first.title调用。

如果你再试一次这样的:

> Product.first; nil 
> Product.first.title 

你会看到两个nil秒。


至于链接去,ActiveRecord的并没有真正检测结束,它只是一些方法调用自然需要从数据库中实际数据和一些不。

如果你打电话whereorder,或任何其他的查询方法,你会得到一个ActiveRecord::Relation实例回来,你可以链中关系对象的详细查询方法和范围。例如,where(其中的ActiveRecord ::关系由包括ActiveRecord::QueryMethods获得)看起来是这样的:

def where(opts, *rest) 
    return self if opts.blank? 

    relation = clone 
    relation.where_values += build_where(opts, rest) 
    relation 
end 

所以它只是使当前查询的副本,增加了一些东西到副本,并为您提供复印件背部。

如果你打电话firstlastto_aall,任何Enumerable方法(即调用each),...那么你在问具体实例,ActiveRecord将不得不执行查询来实现有问题的模型实例。例如,ActiveRecord::Relation#to_a看起来是这样的:

def to_a 
    logging_query_plan do 
    exec_queries 
    end 
end 

all比来包裹to_a多一点。

的ActiveRecord并不真正知道哪里链的末端,它只是不从数据库中加载任何东西,直到它有,所以你告诉它说链端出去,并取回了我一些数据

+0

我认为它确实检测到链条的末端:它是您无法取回查找代理对象的地方! – phoet 2012-03-01 19:58:36

+0

这不是问题所在。问题在于''''''''''''''''''当''''''method_missing''在模型中被使用时'''Product.first''被调用直到''Product.first.title''返回'''nil'''。一旦你调用'''Product.first'''标题返回'''我的示例博客''''。 – user544941 2012-03-01 20:22:39

+0

@ user544941:啊,我明白他在问什么:替换或绕过他的'method_missing'。 – 2012-03-01 20:41:36

相关问题