让我为你想象一下。
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"
这怎么可能?在某种程度上,他们确定方法链的结束并采取行动?至少这就是我的理论。
任何人都可以解释这种行为吗?
答案 0 :(得分:7)
您看到使用irb
来调查事物的工件。
当你这样说时:
> Product.first.title
#=> nil
您的method_missing
将被调用以延迟加载title
方法,并获得nil
。
当你这样说时:
> Product.first
你实际上是这样做的:
> p = Product.first; puts p.inspect
将加载第一个Product实例,然后irb
将在其上调用inspect
,AR将在此过程中添加访问者方法。结果是,产品现在将具有title
方法。因此,这样做:
> Product.first
> Product.first.title
根本不会致电您的method_missing
,因为title
会有真正的Product.first.title
方法来呼叫。
如果你再试这样:
> Product.first; nil
> Product.first.title
您会看到两个nil
。
就链接而言,ActiveRecord并没有真正检测到结束,只是某些方法调用自然需要来自数据库的实际数据而有些则不需要。
如果您调用where
,order
或任何其他查询方法,您将获得ActiveRecord::Relation实例,并且可以在该关系对象上链接更多查询方法和范围。例如,where
(ActiveRecord :: Relation通过包括ActiveRecord::QueryMethods获得)看起来像这样:
def where(opts, *rest)
return self if opts.blank?
relation = clone
relation.where_values += build_where(opts, rest)
relation
end
所以它只是制作当前查询的副本,为副本添加一些内容,并为您提供副本。
如果您致电first
,last
,to_a
,all
,任何Enumerable方法(即您致电each
), ...然后你要问特定的实例,ActiveRecord必须执行查询才能实现有问题的模型实例。例如,ActiveRecord::Relation#to_a
看起来像这样:
def to_a
logging_query_plan do
exec_queries
end
end
和all
只不过是to_a
的包装。
ActiveRecord并不真正知道链的末端在哪里,它只是不加载数据库中的任何东西,所以你必须告诉它链在哪里结束时说出去找我一些数据