[ruby] 루비에서 클래스의 모든 자손을 찾으십시오.

Ruby에서 클래스 계층 구조를 쉽게 올릴 수 있습니다.

String.ancestors     # [String, Enumerable, Comparable, Object, Kernel]
Enumerable.ancestors # [Enumerable]
Comparable.ancestors # [Comparable]
Object.ancestors     # [Object, Kernel]
Kernel.ancestors     # [Kernel]

계층 구조를 내릴 수있는 방법이 있습니까? 나는 이것을하고 싶다

Animal.descendants      # [Dog, Cat, Human, ...]
Dog.descendants         # [Labrador, GreatDane, Airedale, ...]
Enumerable.descendants  # [String, Array, ...]

그러나 방법이없는 것 같습니다 descendants.

(이 질문은 기본 클래스에서 내려온 Rails 응용 프로그램의 모든 모델을 찾아서 나열하려고하기 때문에 발생합니다. 그러한 모델에서 작동 할 수있는 컨트롤러가 있으며 새 모델을 추가하고 싶습니다. 컨트롤러를 수정할 필요가 없습니다.)



답변

예를 들면 다음과 같습니다.

class Parent
  def self.descendants
    ObjectSpace.each_object(Class).select { |klass| klass < self }
  end
end

class Child < Parent
end

class GrandChild < Child
end

puts Parent.descendants
puts Child.descendants

Parent.descendants는 다음을 제공합니다.

GrandChild
Child

Child.descendants가 제공하는 내용 :

GrandChild


답변

Rails> = 3을 사용하면 두 가지 옵션이 있습니다. .descendants하나 이상의 수준의 하위 클래스를 원하거나 .subclasses첫 번째 하위 클래스에 사용하려는 경우에 사용하십시오 .

예:

class Animal
end

class Mammal < Animal
end

class Dog < Mammal
end

class Fish < Animal
end

Animal.subclasses #=> [Mammal, Fish] 
Animal.descendants  #=> [Dog, Mammal, Fish]


답변

멋진 체인 반복자가 포함 된 Ruby 1.9 (또는 1.8.7) :

#!/usr/bin/env ruby1.9

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

루비 pre-1.8.7 :

#!/usr/bin/env ruby

class Class
  def descendants
    result = []
    ObjectSpace.each_object(::Class) {|klass| result << klass if klass < self }
    result
  end
end

다음과 같이 사용하십시오.

#!/usr/bin/env ruby

p Animal.descendants


답변

inherited 라는 클래스 메서드를 재정의하십시오 . 이 메소드는 생성 될 때 추적 할 수있는 서브 클래스로 전달됩니다.


답변

또는 (ruby 1.9+로 업데이트) :

ObjectSpace.each_object(YourRootClass.singleton_class)

루비 1.8 호환 방식 :

ObjectSpace.each_object(class<<YourRootClass;self;end)

모듈에서는 작동하지 않습니다. 또한 YourRootClass가 답변에 포함됩니다. Array #-또는 다른 방법으로 제거 할 수 있습니다.


답변

오브젝트 공간의 작품을 사용하지만, 상속 클래스 메소드는 여기에 더 적합한 것 같다 (서브 클래스) 루비 문서 상속

객체 공간은 본질적으로 현재 할당 된 메모리를 사용하는 모든 것에 액세스 할 수있는 방법이므로 Animal 클래스의 하위 요소인지 여부를 확인하기 위해 모든 요소 중 하나를 반복하는 것이 이상적이지 않습니다.

아래 코드에서 상속 된 Animal 클래스 메서드는 새로 만든 하위 클래스를 하위 배열에 추가하는 콜백을 구현합니다.

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

  def self.descendants
    puts @descendants
  end
end


답변

상속 에서이 작업을 수행하는 방법을 묻고 있지만 루비에서 직접 이름을 지정하여 클래스의 이름을 지정 하여이 작업을 수행 할 수 있습니다 ( Class또는 Module)

module DarthVader
  module DarkForce
  end

  BlowUpDeathStar = Class.new(StandardError)

  class Luck
  end

  class Lea
  end
end

DarthVader.constants  # => [:DarkForce, :BlowUpDeathStar, :Luck, :Lea]

DarthVader
  .constants
  .map { |class_symbol| DarthVader.const_get(class_symbol) }
  .select { |c| !c.ancestors.include?(StandardError) && c.class != Module }
  # => [DarthVader::Luck, DarthVader::Lea]

ObjectSpace다른 솔루션이 제안하는 것처럼 모든 클래스와 비교하는 것보다 훨씬 빠릅니다 .

상속에서 이것을 심각하게 필요로한다면 다음과 같이 할 수 있습니다 :

class DarthVader
  def self.descendants
    DarthVader
      .constants
      .map { |class_symbol| DarthVader.const_get(class_symbol) }
  end

  class Luck < DarthVader
    # ...
  end

  class Lea < DarthVader
    # ...
  end

  def force
    'May the Force be with you'
  end
end

여기에 벤치 마크 :
http://www.eq8.eu/blogs/13-ruby-ancestors-descendants-and-other-annoying-relatives

최신 정보

결국 당신이해야 할 일은 이것입니다

class DarthVader
  def self.inherited(klass)
    @descendants ||= []
    @descendants << klass
  end

  def self.descendants
    @descendants || []
  end
end

class Foo < DarthVader
end

DarthVader.descendants #=> [Foo]

제안 해 주셔서 감사합니다 @ saturnflyer