在Ruby中查找类的所有后代

Dou*_*rel 138 ruby

我可以轻松地提升Ruby中的类层次结构:

String.ancestors     # [String, Enumerable, Comparable, Object, Kernel]
Enumerable.ancestors # [Enumerable]
Comparable.ancestors # [Comparable]
Object.ancestors     # [Object, Kernel]
Kernel.ancestors     # [Kernel]
Run Code Online (Sandbox Code Playgroud)

有没有办法下降层次结构?我想这样做

Animal.descendants      # [Dog, Cat, Human, ...]
Dog.descendants         # [Labrador, GreatDane, Airedale, ...]
Enumerable.descendants  # [String, Array, ...]
Run Code Online (Sandbox Code Playgroud)

但似乎没有一种descendants方法.

(出现这个问题是因为我想找到Rails应用程序中的所有模型,这些模型来自基类并列出它们;我有一个控制器可以使用任何这样的模型,我希望能够添加新模型无需修改控制器.)

Pet*_*ros 138

这是一个例子:

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
Run Code Online (Sandbox Code Playgroud)

puts Parent.descendants为您提供:

GrandChild
Child
Run Code Online (Sandbox Code Playgroud)

puts Child.descendants给你:

GrandChild
Run Code Online (Sandbox Code Playgroud)

  • 如果你的类可能还没有在内存中加载,请注意,`ObjectSpace`不会有它. (19认同)
  • `singleton_class` 而不是 `Class` 使其速度更快(请参阅 http://apidock.com/rails/Class/descendants 的源代码) (2认同)

dgi*_*rez 57

如果使用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]
Run Code Online (Sandbox Code Playgroud)

  • 请注意,在开发过程中,如果关闭了切换加载,这些方法只会在已加载的情况下返回类(即,如果它们已被正在运行的服务器引用). (5认同)
  • @stephen.hanson 保证正确结果的最安全方法是什么? (4认同)
  • 在开发模式下,使用“require_dependency”在调用“.subclasses”、“.descendants”之前自动加载任何必需的文件。要获取所需的路径,请使用路径或 glob,例如“Rails.root.glob('pattern/to/*/file_*.rb)”。它甚至可以在初始化程序中完成,如此处答案中所述:/sf/ask/2076376291/ (3认同)

Jör*_*tag 26

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
Run Code Online (Sandbox Code Playgroud)

Ruby 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
Run Code Online (Sandbox Code Playgroud)

像这样使用它:

#!/usr/bin/env ruby

p Animal.descendants
Run Code Online (Sandbox Code Playgroud)

  • 这也适用于模块; 只需在代码中将"Class"的两个实例替换为"Module". (3认同)
  • 为了更加安全,我们应该编写`ObjectSpace.each_object(:: Class)` - 当你碰巧定义了一个YourModule :: Class时,这将使代码保持工作. (2认同)

Cha*_*kar 19

覆盖名为inherited的类方法.创建它时,此方法将传递给子类,您可以跟踪它.


ape*_*ros 12

或者(更新为ruby 1.9+):

ObjectSpace.each_object(YourRootClass.singleton_class)
Run Code Online (Sandbox Code Playgroud)

Ruby 1.8兼容方式:

ObjectSpace.each_object(class<<YourRootClass;self;end)
Run Code Online (Sandbox Code Playgroud)

请注意,这不适用于模块.此外,YourRootClass将包含在答案中.您可以使用Array# - 或其他方式将其删除.


小智 9

虽然使用ObjectSpace工作,但继承的类方法似乎更适合这里继承的(子类)Ruby文档

Objectspace本质上是一种访问当前使用已分配内存的任何东西和所有内容的方法,因此迭代它的每一个元素以检查它是否是Animal类的子类并不理想.

在下面的代码中,继承的Animal类方法实现了一个回调,它将任何新创建的子类添加到其后代数组中.

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

  def self.descendants
    puts @descendants 
  end
end
Run Code Online (Sandbox Code Playgroud)

  • `@descendants || = []`否则你只会得到最后一个后代 (4认同)