2016-01-15 3 views
7

Wenn ich 4 Klassen mit der folgenden Hierarchie habe:Wie bekomme ich eine Liste aller Unterklassen, ohne zuerst in Ruby Instanziieren

class MainClass < ActiveRecord::Base 
    ... 
end 

class SubClassA < MainClass 
    ... 
end 

class SubClassB < MainClass 
    ... 
end 

class SubClassC < MainClass 
    ... 
end 

Wie kann ich eine Liste der Unterklassen von Mainclass erhalten, ohne durch zu gehen und die Schaffung Instanzen von jeder der anderen Klassen?

In einem frischen IRB-Sitzung kann ich gehen und sagen

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

Allerdings, wenn ich durchlaufen und Instanzen jeder Unterklasse erstellen ich folgendes

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

ich im Grunde sehen werde ich Nach einer Möglichkeit suchend, alle Subklassen programmatisch so zu liefern, dass ich in Zukunft SubClassD, SubClassE usw. hinzufügen möchte, muss ich nicht befürchten, dass jeder in dem Code instanziiert wird, bevor der Benutzer sie sehen kann.

+0

Ich versuchte auf der Schiene Konsole (Schienen 4.1.2). Ich bekomme das (und keine leere Liste wie Sie): - irb (main): 012: 0> MainClass.descendants => [SubClassA (Tabelle existiert nicht), SubClassB (Tabelle existiert nicht)] – rohan

+0

Interessant. Ursprünglich teilte ich meine Unterklassen in ihre eigenen Dateien (sub_class_a.rb, sub_class_b.rb, sub_class_c.rb) alle auf dem gleichen Niveau und ich bekam, was ich oben beschrieben habe. Als ich die Klassen alle in derselben main_class.rb konsolidierte, funktionierte die Methode der Nachkommen wie gesagt. – user1535152

Antwort

7

Dies ist ein Artefakt nur Entwicklungsmodus Klassen geladen, wenn das erste Mal referenziert: diese Dateien sind nicht von der Dolmetscher noch gelesen wurde - so weit wie Rubin die Klassen wirklich existieren noch nicht betroffen ist

eine Abhilfe ist

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

Am unteren Ende der Datei für Hauptklasse zu setzen (außerhalb der Klassendefinition)

+2

Ist diese Lösung Schienen speziell? –

+1

Es ist - sowohl die Abkömmlinge Methode, die Fehlverhalten und Require_dependency sind Schienen spezifisch. –

+0

Wusste das nicht, danke für die Klarstellung. –

7

Es gibt mehrere Möglichkeiten, dies zu erreichen. Entweder durch ein Juwel wie die descendants_tracker mit:

class MainClass < ActiveRecord::Base 
    extend DescendantsTracker 
end 

class SubClassA < MainClass 
end 

... 

MainClass.descendants # => [SubClassA] 

Oder Sie könnten ein benutzerdefinierte in Ihrer Elternklasse wie this erstellen:

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 

Sie auch für die Ebene von Abhängigkeiten überprüfen. Wenn Sie eine Reihe von direktem Kind von Mainclass zum Beispiel wollen könnten Sie die .subclasses Klassenmethode verwenden

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

Nun, wenn Sie mehr als eine Ebene Tiefe von Klassen verwenden .descendants Kind wollen. Sie können ein Beispiel finden here

+0

'

+0

Bedeutet, dass die Methoden '.subclasses' und' .descendants' in einem ActiveRecord-Modell nicht aufgerufen werden können? – Cyzanfar

+0

Nein, ich meine, Ihr Beispiel funktioniert gut, auch wenn 'MainClass' kein ActiveRecord-Modell ist. Daher fügt es keinen Wert hinzu - außer es kann etwas Verwirrung hinzufügen. –

1

Sie ObjectSpace#each_object verwenden könnte eine Liste vonzu kompilieren‚s Kinder:

Angenommen:

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

Die Tatsache, dass MainClass eine Unterklasse irrelevant sein kann.

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