2016-01-15 10 views
7

Jeśli mam 4 klas z następującą hierarchią:Jak mogę uzyskać listę wszystkich podklas bez instancji pierwszy w Ruby

class MainClass < ActiveRecord::Base 
    ... 
end 

class SubClassA < MainClass 
    ... 
end 

class SubClassB < MainClass 
    ... 
end 

class SubClassC < MainClass 
    ... 
end 

Jak mogę uzyskać listę podklas MainClass bez przechodzenia przez i tworzenia wystąpienia każdej z pozostałych klas?

w świeżej IRB sesji mogę iść i powiedzieć

irb(main)> MainClass.descendants 
=> [] 

Jednak gdybym przejść i tworzenia wystąpień każdej podklasy Zobaczę następujące

irb(main)> SubClassA.new 
=> #<SubClassA ...> 
irb(main)> SubClassB.new 
=> #<SubClassB ...> 
irb(main)> SubClassC.new 
=> #<SubClassC ...> 
irb(main)> MainClass.descendants 
=> [SubClassA(...), SubClassB(...), SubClassC(...)] 

jestem w zasadzie poszukując sposobu na programowe dostarczanie wszystkich podklas, więc w przyszłości, gdy chcę dodać SubClassD, SubClassE itp., nie będę musiał się martwić, że każdy jest tworzony w kodzie, zanim użytkownik będzie mógł je zobaczyć.

+0

Próbowałem na konsoli szynowej (szyny 4.1.2). Otrzymuję to (i nie taką pustą listę jak ty): - irb (główne): 012: 0> MainClass.descendants => [SubClassA (tabela nie istnieje), SubClassB (tabela nie istnieje)] – rohan

+0

Interesujące. Pierwotnie podzieliłem moje podklasy na własne pliki (sub_class_a.rb, sub_class_b.rb, sub_class_c.rb) na tym samym poziomie i otrzymałem to, co opisałem powyżej. Kiedy skonsolidowałem wszystkie klasy w tej samej klasie głównej, metoda potomków działała tak, jak powiedziałeś. – user1535152

Odpowiedz

7

To jest artefaktem trybie rozwoju tylko ładowanie klas po raz pierwszy odwołuje: pliki te nie zostały odczytane przez tłumacza jeszcze - o ile rubinowy dotyczy klas naprawdę nie istnieje jeszcze

Rozwiązaniem jest umieszczenie

require_dependency "subclass_a" 
require_dependency "subclass_b" 
.... 

na dole pliku do głównej klasy (poza definicją klasy)

+2

Czy to rozwiązanie jest specyficzne dla RAILS? –

+1

To jest - obie metody potomków, które źle się zachowują i wymagają zależności, są specyficzne dla szyn. –

+0

Nie wiedziałem tego, dziękuję za wyjaśnienia. –

7

Istnieje kilka sposobów na osiągnięcie tego celu. Albo za pomocą gem takich jak descendants_tracker:

class MainClass < ActiveRecord::Base 
    extend DescendantsTracker 
end 

class SubClassA < MainClass 
end 

... 

MainClass.descendants # => [SubClassA] 

Lub można utworzyć niestandardowy jeden w swojej klasie nadrzędnej jak this:

class MainClass < ActiveRecord::Base 
    def self.descendants 
    ObjectSpace.each_object(Class).select { |klass| klass < self } 
    end 
end 

class SubClassA < MainClass 
end 

class SubClassB < MainClass 
end 

puts MainClass.descendants 

#=> SubClassA 
    SubClassB 

Można również sprawdzić poziom zależności. Jeśli chcesz tablicę bezpośredniego dziecka MainClass Na przykład można użyć metody .subclasses klasy

MainClass.subclasses # => [SubClassA, SubClassB,...] 

Teraz, jeśli chcesz więcej niż jeden poziom głębokości klas potomnych używać .descendants. Można znaleźć przykład here

+0

'

+0

Znaczenie metod '.ubclasses' i' .descendants' nie może zostać wywołanych w modelu ActiveRecord? – Cyzanfar

+0

Nie, mam na myśli twój przykład będzie działał dobrze, nawet jeśli 'MainClass' nie był modelem ActiveRecord. W związku z tym nie dodaje żadnej wartości - z wyjątkiem może powodować pewne zamieszanie. –

1

można użyć ObjectSpace#each_object skompilować listędziecidydaktycznego:

Załóżmy:

class MainClass   ; end 
class SubClassA < MainClass; end 
class SubClassB < MainClass; end 
class SubClassC < SubClassB; end 

Fakt MainClass może być podklasą jest nieistotna.

ObjectSpace.each_object(Class).select { |o| o < MainClass }. 
    tap { |siblings| siblings.reject! { |klass| siblings.any? { |k| klass < k } } } 
    #=> [SubClassB, SubClassA] 
Powiązane problemy