2012-12-28 14 views
12

我用Rails 3.2.0爲什麼Rails模型關聯結果不是自然的ActiveRecord :: Relations?

比方說,我有:

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時Concat的多個查詢構建延遲加載查詢。

+0

什麼版本的Rails? –

+0

@AndrewMarshall 3.2.0 –

+1

如果我沒記錯的話,類方法對你說謊 - 它是委託給目標,這是一個數組 –

回答

15

這是一個ActiveRecord::Relation,但是Rails故意騙你。您可以在方法調用已經看到這一點,並繼續通過調用ancestors,其中包括ActiveRecord的類擺看看吧:

c1.articles.ancestors.select { |c| c.to_s =~ /ActiveRecord/ }.size #=> 35 

這表明它是非常Array

發生這種情況是因爲您撥打c1.articles時得到的回覆是ActiveRecord::Associations::CollectionProxy *,其中undefines class(以及許多其他方法)。這意味着class通過its method_missing獲得授權,其中sends it to target。正如我們所看到的,階級的target這裏,其實Array

c1.articles.target.class #=> Array 

這就是c1.articles.class從何而來。儘管如此,它ActiveRecord::Relation

* 我們能證明,這的確是一個ActiveRecord::Associations::CollectionProxy通過調用Ruby的有問題的對象原來class方法:Object.instance_method(:class).bind(c1.articles).call。這是驗證對象沒有試圖假裝爲不同類的好方法。

+0

巧妙的把戲,沒有想到那個之前 –

+0

@AndrewMarshall - 我很想讓你的想法在這種情況下:http://stackoverflow.com/questions/16927437/why-does-my-activerecord-scope-with-merge-return-an-array –

+3

找到這個問題/答案恢復了我的理智後,與Array的衣服整個星期都有一個CollectionProxy。 :) – woodardj

2

因爲當你定義的關聯,它在你的模型地方:

def #{name}(*args) 
    association(:#{name}).reader(*args) 
end 

.reader()返回AssociationProxy,從而消除了的.class方法和代表未知的方法來通過@target。 method_missing