我正在使用Rails 3.2.0
假设我有:
class Comment < ActiveRecord::Base
has_many :articles
end
c1 = Comment.last
然后
c1.articles.class
# => Array
c1.articles.where('id NOT IN (999999)').class
# => ActiveRecord::Relation
为什么关联的结果不是一种ActiveRecord::Relation
?
它显然是/曾经在某个时候:
c1.articles.to_orig
# undefined method `to_orig' for #<ActiveRecord::Relation:0x007fd820cc80a8>
c1.articles.class
# => Array
某些计算作用于 ActiveRecord::Relation 对象,但检查类会给出不同的类型。
特别是,这会在使用merge
连接多个查询时中断构建延迟加载的查询。
ActiveRecord::Relation
,但Rails故意骗你。您可以在方法调用中看到这一点,并通过调用 ancestors
继续看到它,其中包括大量 ActiveRecord 类:
c1.articles.ancestors.select { |c| c.to_s =~ /ActiveRecord/ }.size #=> 35
这表明它在很大程度上不是一个Array
.
发生这种情况是因为您在调用c1.articles
时得到的是一个ActiveRecord::Associations::CollectionProxy
*,它取消了class
的定义(以及许多其他方法(。这意味着class
通过其method_missing
被委托,将其发送到target
。正如我们所看到的,这里的target
类实际上是Array
:
c1.articles.target.class #=> Array
这就是c1.articles.class
的来源。尽管如此,这是一个ActiveRecord::Relation
.
* 我们可以通过在相关对象上调用 Ruby 的原始 class
方法来验证它确实是一个ActiveRecord::Associations::CollectionProxy
:Object.instance_method(:class).bind(c1.articles).call
。这是一个很好的技巧,可以验证对象是否没有尝试伪装成不同的类。
因为当您定义关联时,它会在您的模型中放置:
def #{name}(*args)
association(:#{name}).reader(*args)
end
.reader(( 返回 AssociationProxy,它将删除 .class 方法并将未知方法委托给通过.method_missing@target。