0

Note that my question is different from this question because I'm asking for all descendants (including descendants of descendants) of a class.

Moreover, I would prefer to use something like

class Animal   
  def self.inherited(subclass)
    @descendants = []
    @descendants << subclass   
  end

  def self.descendants
    puts @descendants    
  end
end

because it's way faster than getting all classes and filtering for descendants.

Community
  • 1
  • 1
Cisplatin
  • 2,860
  • 3
  • 36
  • 56
  • 1
    Lazy instantiate `@descendants ||= []` and you are done. This won’t work out of the box for descendants of descendants, though. The answer linked _does the trick_ for the whole tree (descendants of descendants.) – Aleksei Matiushkin Apr 03 '17 at 15:37

1 Answers1

1
class A
  singleton_class.send(:attr_reader, :descendants)
  @descendants = []
  def self.inherited(subclass)
    A.descendants << subclass
  end
end

A.methods(false)
  #=> [:inherited, :descendants, :descendants=]

class B < A; end
class C < B; end
class D < B; end
class E < C; end
class F < E; end

A.descendants
  #=> [B, C, D, E, F]

Alternatively, you can use ObjectSpace#each_object to obtain A's descendants.

ObjectSpace.each_object(Class).select { |c| c < A }
  #=> [F, E, D, C, B]

If you wish to obtain the ordering of

arr = [B, C, D, E, F]

you could write

(arr << A).each_with_object({}) { |c,h| h[c] =
  arr.each_with_object([]) { |cc,a| a << cc if cc.superclass == c } }
  #=> {B=>[C, D], C=>[E], D=>[], E=>[F], F=>[], A=>[B]}
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100