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

时间:2015-04-05 16:14:44

标签: ruby-on-rails ruby static-analysis

我想知道是否有可能获得从给定Class派生的类列表。

我知道,有一个回调Class::inherited,“只要创建当前类的子类就会调用它。”这种方法存在两个问题:

  1. 当我不是这个类的制作人(那个说,我必须monkeypatch它)时,我通常不能确保在创建第一个派生类之前应用我的monkeypatch。
  2. 在完美的世界中,我会得到类列表,不管它们是否已经初始化,而实际上在实例化类时调用回调
  3. 我理解,RTTI可能不是检索我需要的信息的最佳方式(因为上面的2.)。有人会提出另一种方法吗?静态代码分析?任何?

    我真的很感激任何想法。说,我在我的目录中拥有所有感兴趣的代码(换句话说,我对我的类感兴趣,仅从一些预定义的类派生,例如我的Rails应用程序中的ApplicationController。 )

5 个答案:

答案 0 :(得分:6)

如何使用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

由于

答案 1 :(得分:2)

您可以使用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]}]}

答案 2 :(得分:0)

尝试:

ApplicationController.subclasses

答案 3 :(得分:0)

你可以使用Class#&lt;和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,subclasses,derived_classes,children_rec,descendants?

答案 4 :(得分:0)

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

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

▶ derived User
#⇒ [Admin, Editor]