2012-12-28 11 views
12

Ich bin mit Rails 3.2.0Warum sind Rails Modell Assoziation Ergebnisse nicht natürlich ActiveRecord :: Relations?

Lasst uns sagen, ich habe:

class Comment < ActiveRecord::Base 
    has_many :articles 
end 

c1 = Comment.last 

dann

c1.articles.class 
# => Array 

c1.articles.where('id NOT IN (999999)').class 
# => ActiveRecord::Relation  

Warum das Ergebnis eines Vereins ist nicht eine Art von ActiveRecord::Relation ?

Es eindeutig ist/war irgendwann:

c1.articles.to_orig 
# undefined method `to_orig' for #<ActiveRecord::Relation:0x007fd820cc80a8> 

c1.articles.class 
# => Array 

Bestimmte Auswertungen wirken auf eine Active :: Relation Objekt, sondern die Klasse Inspektion gibt einen anderen Typ.


Insbesondere diese bricht faul belastetes Abfragen bauen, wenn merge mit mehreren Abfragen verketten. Da

+0

Welche Version von Rails? –

+0

@AndrewMarshall 3.2.0 –

+1

Wenn ich mich richtig erinnere, lügt die Klassenmethode - sie delegiert zum Ziel, das ist ein Array –

Antwort

15

Es ist ein ActiveRecord::Relation, aber Rails ist absichtlich zu Ihnen gelogen. Sie können dies bereits in den Methodenaufrufen sehen, und auch weiterhin sehen von ancestors Aufruf, die eine ganze Reihe von Active Klassen umfasst:

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

was zeigt, dass es nicht ein Array sehr viel.

Dies passiert, weil das, was Sie zurückrufen, wenn Sie c1.articles aufrufen, ein ActiveRecord::Associations::CollectionProxy * ist, das undefines class (zusammen mit vielen anderen Methoden). Dies bedeutet, dass class über its method_missing delegiert wird, welche sends it to target. Wie wir in der Tat Array sehen können, ist die Klasse der target hier:

c1.articles.target.class #=> Array 

Das ist, wo c1.articles.class herkommt. Dennoch ist es ist ein ActiveRecord::Relation.

* Wir können bestätigen, dass es sich tatsächlich um eine ActiveRecord::Associations::CollectionProxy von Rubys ursprünglichen class Methode für das Objekt in Frage zu stellen: Object.instance_method(:class).bind(c1.articles).call. Dies ist ein netter Trick, um zu überprüfen, dass das Objekt nicht versucht, so zu tun, als ob es einer anderen Klasse angehört.

+0

Ordentlicher Trick, hatte noch nicht daran gedacht –

+0

@AndrewMarshall - Ich würde gerne Bekommen Sie Ihre Gedanken über diese Situation: http://StackOverflow.com/Questions/16927437/Why-does-my-activerecord-Scope-with-merge-ReRurn-an-Array –

+3

Das Finden dieser Frage/Antwort stellte meine geistige Gesundheit nach dem Kampf mit wieder her Ein CollectionProxy in Arrays Kleidung die ganze Woche. :) – woodardj

2

beim Verein definieren, legt es in Ihrem Modell:

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

.reader() AssociationProxy zurückgibt, die die .class- Verfahren und Delegierten unbekannte Methoden durch @Target entfernt. method_missing.

Verwandte Themen