2009-07-08 30 views
9

ben ActiveRecord çocuk nesneler topluluğu içeren bir nesne, yaniYakut tipleri

class Foo < ActiveRecord::Base 
    has_many :bars, ... 
end 

var ve bu koleksiyon karşı Dizisinin find yöntemini çalıştırmayı denerseniz:

foo_instance.bars.find { ... } 

Alıyorum:

ActiveRecord::RecordNotFound: Couldn't find Bar without an ID 

Bunun nedeni, ActiveRecord'un'yı kaçırmasıdır Kendi amaçları içinyöntemi. Şimdi detect kullanabilirim ve her şey yolunda. Ancak kendi curiousity tatmin etmek, açıkça bir koşuya find yöntemi geri çalmaya metaprogramming kullanmayı denedi:

unbound_method = [].method('find').unbind 
unbound_method.bind(foo_instance.bars).call { ... } 

ve bu hatayı alıyorsunuz:

TypeError: bind argument must be an instance of Array 

çok net Yakut düşünmüyor

foo_instance.bars.instance_of?(Array) -> true 

herkes metaprogramm ile etrafında almak için bu ve bir bakıma bir açıklama ile bana yardımcı olabilir

: foo_instance.bars henüz Dizi ve olduğu ing?

cevap

6

Diğerlerinin dediği gibi, bir ilişkilendirme nesnesi aslında bir Array değildir.Gerçek sınıf öğrenmek için irb bunu:

class << foo_instance.bars 
    self 
end 
# => #<Class:#<ActiveRecord::Associations::HasManyAssociation:0x1704684>> 

ActiveRecord::Associations::HasManyAssociation.ancestors 
# => [ActiveRecord::Associations::HasManyAssociation, ActiveRecord::Associations::AssociationCollection, ActiveRecord::Associations::AssociationProxy, Object, Kernel] 

ActiveRecord :: Eğer foo_instance.bars.find() ne zaman aşağıdakiler yardımcı olacaktır çağrılan Bse # bulmak yönteminin kurtulmak için: yaklaşık bilmiyor AssociationProxy sınıfı delegeler tüm yöntemler için

class << foo_instance.bars 
    undef find 
end 
foo_instance.bars.find {...} # Array#find is now called 

Bu

fiili temel dizi örneği kendi #target için (method_missing ile). Burada netleştirmek için

9

Bu gerçekten gerçek bir açıklama değil. foo_instance.bars, bir Array örneğini değil, ActiveRecord::Associations::AssociationProxy örneğini döndürmez. Bu, ilişkilendirmeyi ve ilişkilendirilmiş nesneyi tutan nesne arasında bir proxy olarak davranması amaçlanan özel bir sınıftır.

AssociatioProxy nesnesi bir dizi gibi davranır, ancak aslında bir dizi değildir. Aşağıdaki detaylar doğrudan dokümantasyondan alınmıştır.

# Association proxies in Active Record are middlemen between the object that 
# holds the association, known as the <tt>@owner</tt>, and the actual associated 
# object, known as the <tt>@target</tt>. The kind of association any proxy is 
# about is available in <tt>@reflection</tt>. That's an instance of the class 
# ActiveRecord::Reflection::AssociationReflection. 
# 
# For example, given 
# 
# class Blog < ActiveRecord::Base 
#  has_many :posts 
# end 
# 
# blog = Blog.find(:first) 
# 
# the association proxy in <tt>blog.posts</tt> has the object in +blog+ as 
# <tt>@owner</tt>, the collection of its posts as <tt>@target</tt>, and 
# the <tt>@reflection</tt> object represents a <tt>:has_many</tt> macro. 
# 
# This class has most of the basic instance methods removed, and delegates 
# unknown methods to <tt>@target</tt> via <tt>method_missing</tt>. As a 
# corner case, it even removes the +class+ method and that's why you get 
# 
# blog.posts.class # => Array 
# 
# though the object behind <tt>blog.posts</tt> is not an Array, but an 
# ActiveRecord::Associations::HasManyAssociation. 
# 
# The <tt>@target</tt> object is not \loaded until needed. For example, 
# 
# blog.posts.count 
# 
# is computed directly through SQL and does not trigger by itself the 
# instantiation of the actual post records. 

Sonuç dizisi üzerinde çalışmak istiyorsanız, meta programlama becerilerine hiç ihtiyacınız yoktur. Sadece sorguyu yapın ve gerçek bir Array nesnesinde bulun yöntemini çağırarak, quacks like an array numaralı örneğe bakın.

foo_instance.bars.all.find { ... } 

all yöntem ActiveRecord bulucu yöntemi (all) bulmak için bir kısayol() 'dir. Bir array sonuç döndürür. Ardından dizi örneğinde Array#find yöntemini çağırabilirsiniz.

+1

, .all yöntem aslında dernek türüne göre çok büyük bir hafıza etkisi olabilir tüm ilişkili modelleri alır. Örneğin, User has_many: posts ise, kullanıcının tüm yayınlama geçmişini alıyor olabilirsiniz. Bu, önemli miktarda veri olabilir. Mümkün olduğunda, daha iyi performans için koşullarla veya adlandırılmış kapsamlarla bir arama çağrısı oluşturmaya çalışın. – tadman

3

ActiveRecord dernekleri, örnek yansımalarını geçersiz kılan Yansıma örnekleridir. ve sınıfın ne olduğu hakkında yalan söyleme yöntemleri. Bu yüzden adlandırılmış kapsamlar ekleme (örneğin, "son") ve foo_instance.bars.recent çağırmak gibi şeyler yapabilirsiniz. Eğer "çubuklar" bir Array olsaydı, bu oldukça zor olurdu.

Kaynak kodunu denetlemeyi deneyin ("yansımaları.rb bulun" unix-ish kutusunda izlemesi gerekir). Chad Fowler bu konuda çok bilgilendirici bir konuşma yaptı, ancak web'de herhangi bir bağlantı bulamıyorum. (Bu gönderiyi eklemek istedikleriniz var mı?)