ActiveRecord如何检测链中的最后一个方法调用?

问题描述 投票:5回答:1

让我为你想象一下。

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"

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

谁能解释这种行为?

ruby activerecord method-chaining
1个回答
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

你会看到两个nils。


就链接而言,ActiveRecord并没有真正检测到结束,只是某些方法调用自然需要来自数据库的真实数据而有些则不需要。

如果你调用whereorder或任何其他查询方法,你会得到一个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

所以它只是制作当前查询的副本,为副本添加一些内容,并为您提供副本。

如果你调用firstlastto_aall,任何Enumerable方法(即你调用each),...那么你问的是特定的实例,ActiveRecord必须执行查询才能实现模型实例有问题。例如,ActiveRecord::Relation#to_a看起来像这样:

def to_a
  logging_query_plan do
    exec_queries
  end
end

all只不过是to_a的包装。

ActiveRecord并不真正知道链的末尾是什么,它只是不加载数据库中的任何内容,直到它必须通过说出去并检索我一些数据来告诉它链的结束位置。

© www.soinside.com 2019 - 2024. All rights reserved.