色々行き詰まっているので、誰得な新機能を入れました。Rubyのしくみ読者用。
例えば、嘘偽りの無い ancestors が欲しい時はこうします。
require 'objspace' def ObjectSpace.internal_ancestors_of klass ancestors = [] while klass ancestors << klass klass = ObjectSpace.internal_super_of(klass) end ancestors end require 'pp' pp String.ancestors pp ObjectSpace.internal_ancestors_of(String)
結果。
[String, Comparable, Object, PP::ObjectMixin, Kernel, BasicObject] [String, #<InternalObject:0x7c671c T_ICLASS>, Object, #<InternalObject:0x282eb58 T_ICLASS>, #<InternalObject:0x7c7568 T_ICLASS>, BasicObject]
ほうら、わかりやすい。
クラス / モジュールの参照図を書いてみるのも分かりやすい。
require 'objspace' require 'pp' def ObjectSpace.object_id_of obj if obj.kind_of?(ObjectSpace::InternalObjectWrapper) obj.internal_object_id else obj.object_id end end class Module def references h = {} # object_id -> [klass, class_of, super] stack = [self] while klass = stack.pop obj_id = ObjectSpace.object_id_of(klass) next if h.has_key?(obj_id) cls = ObjectSpace.internal_class_of(klass) sup = ObjectSpace.internal_super_of(klass) stack << cls if cls stack << sup if sup h[obj_id] = [klass, cls, sup].map{|e| (defined?(e.name) && e.name) ? e.name : e.inspect} end h.values end end pp String.references
[["String", "#<Class:String>", "#<InternalObject:0x806718 T_ICLASS>"], ["#<InternalObject:0x806718 T_ICLASS>", "Comparable", "Object"], ["Object", "#<Class:Object>", "#<InternalObject:0x271dbc4 T_ICLASS>"], ["#<InternalObject:0x271dbc4 T_ICLASS>", "PP::ObjectMixin", "#<InternalObject:0x807564 T_ICLASS>"], ["#<InternalObject:0x807564 T_ICLASS>", "Kernel", "BasicObject"], ["BasicObject", "#<Class:BasicObject>", "nil"], ["#<Class:BasicObject>", "#<Class:#<Class:BasicObject>>", "Class"], ["Class", "#<Class:Class>", "Module"], ["Module", "#<Class:Module>", "Object"], ["#<Class:Module>", "#<Class:#<Class:Module>>", "#<Class:Object>"], ["#<Class:Object>", "#<Class:#<Class:Object>>", "#<Class:BasicObject>"], ["#<Class:#<Class:Object>>", "#<Class:#<Class:Class>>", "#<Class:#<Class:BasicObject>>"], ["#<Class:#<Class:BasicObject>>", "#<Class:#<Class:Class>>", "#<Class:Class>"], ["#<Class:Class>", "#<Class:#<Class:Class>>", "#<Class:Module>"], ["#<Class:#<Class:Class>>", "#<Class:#<Class:Class>>", "#<Class:#<Class:Module>>"], ["#<Class:#<Class:Module>>", "#<Class:#<Class:Class>>", "#<Class:#<Class:Object>>"], ["Kernel", "#<Class:Kernel>", "nil"], ["#<Class:Kernel>", "Class", "Module"], ["PP::ObjectMixin", "Module", "nil"], ["Comparable", "Module", "nil"], ["#<Class:String>", "#<Class:Class>", "#<Class:Object>"]]
この配列の各要素は m, k, s の配列になっており、m のクラスは k、m の super は s という関係になっています。
で、これはグラフなので、graphviz に食わせるようにしてみると、
require 'objspace' require 'pp' def ObjectSpace.object_id_of obj if obj.kind_of?(ObjectSpace::InternalObjectWrapper) obj.internal_object_id else obj.object_id end end class Module def references h = {} # object_id -> [klass, class_of, super] stack = [self] while klass = stack.pop obj_id = ObjectSpace.object_id_of(klass) next if h.has_key?(obj_id) cls = ObjectSpace.internal_class_of(klass) sup = ObjectSpace.internal_super_of(klass) stack << cls if cls stack << sup if sup h[obj_id] = [klass, cls, sup].map{|e| (defined?(e.name) && e.name) ? e.name : e.inspect} end h.values end end class C end rank_set = {} puts "digraph mod_h {" C.references.each{|(m, s, k)| # next if /singleton/ =~ m puts "#{m.dump} -> #{s.dump} [label=\"super\"];" puts "#{m.dump} -> #{k.dump} [label=\"klass\"];" unless rank = rank_set[m] rank = rank_set[m] = 0 end unless rank_set[s] rank_set[s] = rank + 1 end unless rank_set[k] rank_set[k] = rank end } rs = [] # [[mods...], ...] rank_set.each{|m, r| rs[r] = [] unless rs[r] rs[r] << m } rs.each{|ms| puts "{rank = same; #{ms.map{|m| m.dump}.join(", ")}};" } puts "}"
こんな絵が出ます。
http://www.atdot.net/fp_store/f.z1u2pn/file.g.png
ちょっと複雑なものを食わせてみると、
module M0; end module M1; end class C0; end class C1 < C0 include M0 prepend M1 end
http://www.atdot.net/fp_store/f.wfu2pn/file.g.png
凄いコトに。
方向を弄ってみたら、
http://www.atdot.net/fp_store/f.9nu2pn/file.g.png
ちょっと見やすくなったかな。
k と s の順番間違えてた。