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

Ruby ActiveRecord如何检测链中的最后一个方法调用?,ruby,activerecord,method-chaining,Ruby,Activerecord,Method Chaining,让我为你设想一下 class Product < ActiveRecord::Base end Product.first.title #=> "My sample product" 类产品“我的样品产品” 这里没什么特别的。只是一个简单的方法调用。现在看看下面的例子 class Product < ActiveRecord::Base def method_missing end end Product.first.title #=> nil Prod

让我为你设想一下

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"
类产品零
产品优先
产品名称
#=>“我的样品产品”
这怎么可能?在某种程度上,他们决定了方法链的终点,并据此采取行动?至少这是我的理论


有人能解释这种行为吗

你看到了一个使用
irb
调查事情的假象

当你这么说的时候:

> Product.first.title
#=> nil
> Product.first
您的
方法\u missing
将被调用以延迟加载
title
方法,您将得到
nil

当你这么说的时候:

> Product.first.title
#=> nil
> Product.first
实际上,您正在这样做:

> p = Product.first; puts p.inspect
> Product.first
> Product.first.title
第一个产品实例将被加载,然后
irb
将在其上调用
inspect
,AR将在此过程中添加访问器方法。结果是产品现在将有一个
title
方法。因此,这样做:

> p = Product.first; puts p.inspect
> Product.first
> Product.first.title
不会调用您的
方法\u missing
,因为
产品将有一个真正的
title
方法。首先,要调用title

如果您再次这样尝试:

> Product.first; nil
> Product.first.title
def where(opts, *rest)
  return self if opts.blank?

  relation = clone
  relation.where_values += build_where(opts, rest)
  relation
end
def to_a
  logging_query_plan do
    exec_queries
  end
end
您将看到两个
nil
s


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

如果调用
where
order
或任何其他查询方法,则会返回一个实例,并且可以在该关系对象上链接更多查询方法和范围。例如,(ActiveRecord::Relation通过包含获得的)如下所示:

> Product.first; nil
> Product.first.title
def where(opts, *rest)
  return self if opts.blank?

  relation = clone
  relation.where_values += build_where(opts, rest)
  relation
end
def to_a
  logging_query_plan do
    exec_queries
  end
end
因此,它只是复制当前查询,在副本中添加一些内容,然后返回副本

如果您调用
第一个
最后一个
到a
所有
,任何方法(即调用
每个
)。。。然后,您询问特定实例,ActiveRecord必须执行查询以实现所讨论的模型实例。例如,如下所示:

> Product.first; nil
> Product.first.title
def where(opts, *rest)
  return self if opts.blank?

  relation = clone
  relation.where_values += build_where(opts, rest)
  relation
end
def to_a
  logging_query_plan do
    exec_queries
  end
end
它只不过是
对a
的包装而已


ActiveRecord并不真正知道链的末端在哪里,它只是在必须加载之前不会从数据库中加载任何内容,因此您可以通过说“前进并检索一些数据”来告诉它链的末端。

我认为它确实检测到链的末端:在那里您不会返回finder代理对象!这不是问题所在。问题是为什么
Product.first.title
返回
nil
,直到调用
Product.first
,当在模型中使用
method\u missing
时。一旦你调用了
产品。首先
title返回
我的示例博客
@user544941:啊,我知道他在问什么了:是什么替换或绕过了他的
方法\u缺少
@user544941:我现在有正确的问题了吗?我想他只是被
irb
在背后做的事情弄糊涂了。顺便说一句,谢谢你指出困惑。