Cisplatin
Cisplatin

Reputation: 2998

How to get all descendants of a class in Ruby using `self.inherited`?

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.

Upvotes: 0

Views: 1047

Answers (1)

Cary Swoveland
Cary Swoveland

Reputation: 110665

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]}

Upvotes: 1

Related Questions