2010-10-12 29 views
14

Ben bunu dahil tüm sınıfların listesini almak istiyorum bunun için bir mixin var şunlardır sınıflarının listesini alınıyor.bir modül

module MyModule 
    def self.included(base) 
    @classes ||= [] 
    @classes << base.name 
    end 

    def self.classes 
    @classes 
    end 
end 

class MyClass 
    include MyModule 
end 

Bu oldukça iyi çalışıyor:

> MyModule.classes #=> nil 
> MyClass.new #=> #<MyClass ...> 
> MyModule.classes #=> ["MyClass"] 

Şimdi, benim diğer eklenebilir ayrı bir modül içine bu bölümünü ayıklamak istiyorum mixin modülde, aşağıdaki yaptım Katmalar. Yani, aşağıdaki ile geldi: #include (module_base) yöntemi asla koşmak oluyor ListIncludedClasses gelen MyModule eklenmeden çünkü

module ListIncludedClasses 
    def self.included(base) 
    p "...adding #{base.name} to #{self.name}.classes" 

    @classes ||= [] 
    @classes << base.name 

    base.extend(ClassMethods) 
    end 

    def self.classes 
    @classes 
    end 

    module ClassMethods 
    def included(module_base) 
     p "...adding #{module_base.name} to #{self.name}.classes" 

     @module_classes ||= [] 
     @module_classes << module_base.name 
     super(module_base) 
    end 
    def classes 
     @module_classes 
    end 
    end 

end 

module MyModule 
    include ListIncludedClasses 
end 

Bu olsa çalışmaz. İlginçtir ki, MyModule'e #classes'ı başarıyla ekler.

> MyModule.classes #=> 
    "...adding Rateable to ListIncludedClasses.classes" 
    => nil 
> ListIncludedClasses #=> ["MyModule"] 
> MyClass.new #=> #<MyClass ...> 
# ^^ THIS SHOULD BE ADDING MyClass TO MyModule.classes ^^ 
> MyModule.classes #=> nil 

Neyi eksik? Muhtemelen uzatmak yerine eski, sınıf düzeyi yöntemleri ekler beri dahil kimlerin kullanabileceği

+0

Sorunuzu yanıtladığımı belirtmeyi unutmayın! – Tom

+0

bunu denediniz mi: http://stackoverflow.com/questions/3527445/when-are-modules-included-in-a-ruby-class-running-in-rails – rickypai

cevap

14
module MyMod; end 

class A; include MyMod; end 
class B < A; end 
class C; end 

ObjectSpace.each_object(Class).select { |c| c.included_modules.include? MyMod } 
    #=> [B, A] 
1

iken ikincisi - örnek düzeyinde yöntemleri (sen @classes erişebilir neden).

bu deneyin:

module MyModule 
    extend ListIncludedClasses::ClassMethods 
end 
+2

Bunu kaçırırsanız bilmiyorum, fakat genişletmek (ClassMethods) self.included (base) yöntemindedir, bu yüzden sınıf yöntemlerini genişletmektedir. Eklenmesi gereken diğer örnek yöntemleri vardır; Bu, örnek yöntemlerini ve sınıf yöntemlerini genişletmek için yaygın olarak kabul edilen bir tekniktir. Ayrıca, önerdiğiniz gibi sınıf yöntemlerini doğrudan genişletmek de bir fark yaratmaz. – jangosteve

2

Aslında, modül uzatma modülü eserler. Sorun testinizde: Class.new ile rastgele adlandırılmamış bir sınıf oluşturduğunuzda, MyModule'u eklemeyi unutmuştunuz. Bir yan not olarak, salt okunur erişiminizi, modülü içeren ve yararlı Module#attr_reader yöntemini kullanan sınıflar için alabilirsiniz.

+0

Cevabınız için teşekkür ederiz. Evet, şimdi denediğimde işe yarıyor, o sırada ne yaptığımı bilmiyorum. Sanırım onu ​​izole etmek yerine Rails projesinde yaptım, kim bilir. 'MyClass' tanımını 'include' ile unuttum değildi, sadece yukarıdaki örnekte yeniden yazmamıştım. Aksi takdirde, MyClass.new, başlatılmamış sabit MyClass'ı atmış olur. Ayrıca Modül # attr_reader' örnek yöntemlerini oluşturur, bu yüzden onu 'ClassMethods' içinde ve daha sonra da ListIncludedClasses' içinde bir 'class << self' bloğunun içinde kullanabilirdim. Bu daha verimlidir, bu yüzden üretimde yol olacak. – jangosteve