获取从给定 Class 派生的 类 的列表

Get list of classes derived from given Class

我想知道是否可以从给定的 Class.

中获取 classes 的列表

我明白了,有一个回调 Class::inherited,它将“在创建当前 class 的子 class 时调用。”这种方法有两个问题:

  1. 当我不是这个 class 的生产者时(也就是说,我必须对其进行 monkeypatch,)我通常无法确保我的 monkeypatch 会在第一个派生 class 即将创建。
  2. 在完美世界中,我会得到 classes 的列表,不管它们是否已经初始化,而 回调实际上是在 class 正在实例化.

我明白,RTTI 可能不是检索我需要的信息的最佳方式(因为上面的 2.)。有人会建议另一种方法吗?静态代码分析?任何?

如果有任何想法,我将不胜感激。说,我的目录中有所有感兴趣的代码(换句话说,我对 my classes 感兴趣,它们仅来自一些预定义的 Class,例如 ApplicationControllers 在我的 Rails 应用程序中。)

尝试:

ApplicationController.subclasses

您可以使用 ObjectSpace::each_object:

def derived_classes(klass)
  ObjectSpace.each_object(Class).with_object([]) { |k,a| a << k if k < klass }
end

class A       ; end
class AA  < A ; end
class AB  < A ; end
class AC  < A ; end
class AAA < AA; end
class AAB < AA; end
class ABA < AB; end
class ABB < AB; end

derived = derived_classes(A)
  #=> [AC, AA, AB, AAA, AAB, ABA, ABB] 

编辑:

唉,这不是我们所需要的,但让我提供一种方法,一旦派生的 类 被识别出来,它可能会有用:

def order_classes(top, derived)
  children = derived.select { |k| k.superclass == top }
  return top if children.empty?   
  { top=>children.each_with_object([]) { |k,a|
    a << order_classes(k, derived-children) } }
end

order_classes(A, derived)
  #=> {A=>[AC, {AA=>[AAA, AAB]}, {AB=>[ABA, ABB]}]}

您可以使用 Class#< 和 ObjectSpace#each_object。

class A; end
class B < A; end
class C < A; end
class CC < C; end
ObjectSpace.each_object(Class).select{|c| c < A}
# => [C, B, CC]

class Class
  def subclasses
    ObjectSpace.each_object(Class).select{|c| c < self}
  end
end
A.subclasses
# => [C, B, CC]

我不确定如何命名,inferior_classes,子类,derived_classes,children_rec,后代?

使用 TracePoint 怎么样?让我知道以下代码是否符合您的目的 -

class DerivedClassObserver

  def initialize(classes)
    @classes, @subclasses = classes, {}
  end

  def start
    @trace_point = TracePoint.new(:class) do |tp|
      tp.self.ancestors.map do |ancestor|
        if ancestor != tp.self && @classes.include?(ancestor.name)
          (@subclasses[ancestor.name] ||= []) << tp.self.name
        end
      end
    end

    @trace_point.enable
  end

  def stop
    @trace_point.disable
  end

  def subclasses(class_name)
    @subclasses[class_name]
  end
end

用法示例

observer = DerivedClassObserver.new %w|A AA|
observer.start

# Borrowed example from @Cary
class A       ; end
class AA  < A ; end
class AB  < A ; end
class AC  < A ; end
class AAA < AA; end
class AAB < AA; end
class ABA < AB; end
class ABB < AB; end

observer.stop

puts "A subclasses #{observer.subclasses('A').join(', ')}"
# => A subclasses AA, AB, AC, AAA, AAB, ABA, ABB

puts "AA subclasses #{observer.subclasses('AA').join(', ')}"
# => AA subclasses AAA, AAB

谢谢

丑陋的解决方案,适用于测试:

def derived from
  Dir["#{Rails.root}/**/*.rb"].map do |f|
    File.read(f).match(/(\S+)\s*<\s*#{from}\s/) &&   
  end.compact
end

▶ derived User
#⇒ [Admin, Editor]