読者です 読者をやめる 読者になる 読者になる

[ruby] Rubyで実行中のオブジェクトのクラス階層、各クラスのメソッドを見やすく表示する

※ gem化しました → Rubyのクラス階層やメソッドを美しく表示する、rubywho
※githubに登録しました。→ p_classtree、改良(2)

これは何?

gem等で提供されている開発中のライブラリを自分で使うような場合、ドキュメントが整備中だったり、ドキュメントに載ってない新しい機能が入っていることはよくあります。そのような時はまあソースを読むしかないのですが、そもそもチュートリアルに沿って動かしたあのインスタンスがなんだったのか分からなくて、スタート地点すら分からずに途方にくれることが良くあります(C++等の静的言語と違い、そもそもソースコード中に型を類推する手がかりが少ないというのもあります)。Rubyにはインスタンスの所属するクラスや、そのメソッドを調べる方法が用意されているので、ざっとクラス階層とpublicなメソッド群を調べる関数を作ってみました。

p_classtreeという関数にオブジェクトやクラスを渡すと・・。

# インスタンスを渡した場合はその所属するクラスの階層、メソッドを表示
a = [0, 1, 2]
p_classtree(a)
puts

# クラス名を直接渡してもOK
p_classtree(Hash)

結果はこんな感じで表示されます。

Array
|  &, *, +, -, <<, 
|  <=>, ==, [], []=, abbrev, 
|  assoc, at, choice, clear, collect, 
|  collect!, combination, compact, compact!, concat, 
|  count, cycle, delete, delete_at, delete_if, 
|  drop, drop_while, each, each_index, empty?, 
|  eql?, fetch, fill, find_index, first, 
|  flatten, flatten!, frozen?, hash, include?, 
|  index, indexes, indices, insert, inspect, 
|  join, last, length, map, map!, 
|  nitems, pack, permutation, pop, product, 
|  push, rassoc, reject, reject!, replace, 
|  reverse, reverse!, reverse_each, rindex, select, 
|  shift, shuffle, shuffle!, size, slice, 
|  slice!, sort, sort!, taguri, taguri=, 
|  take, take_while, to_a, to_ary, to_s, 
|  to_yaml, transpose, uniq, uniq!, unshift, 
↓  values_at, yaml_initialize, zip, |, 
Object

Fixnum
|  %, &, *, **, +, 
|  -, -@, /, <, <<, 
|  <=, <=>, ==, >, >=, 
|  >>, [], ^, abs, div, 
|  divmod, even?, fdiv, id2name, modulo, 
|  odd?, quo, size, to_f, to_s, 
↓  to_sym, zero?, |, ~, 
Integer
|  ceil, chr, downto, even?, floor, 
|  integer?, next, odd?, ord, pred, 
|  round, succ, times, to_i, to_int, 
↓  truncate, upto, 
Numeric
|  +@, -@, <=>, abs, ceil, 
|  coerce, div, divmod, eql?, fdiv, 
|  floor, integer?, modulo, nonzero?, quo, 
|  remainder, round, singleton_method_added, step, to_int, 
↓  truncate, zero?, 
Object

ちなみに最近良く触ってるrroongaオブジェクトを表示するとこんな感じ。

Groonga::Hash
↓  search, 
Groonga::Table
|  [], clear_lock, column, column_value, columns, 
|  define_column, define_index_column, delete, difference!, each, 
|  empty?, group, have_column?, inspect, intersection!, 
|  lock, locked?, merge!, open_cursor, paginate, 
|  records, select, set_column_value, set_value, size, 
|  sort, support_sub_records?, truncate, union!, unlock, 
↓  value, 
Groonga::Object
|  ==, [], []=, append, close, 
|  closed?, context, domain, id, inspect, 
|  name, path, persistent?, prepend, range, 
↓  remove, temporary?, 
Object

ソース

def p_classtree(c)
  unless c.is_a?(Class)
    c = c.class
  end
  
  while (true)
    puts c.name
    break if (c == Object)
    p_classtree_sub(c)
    c = c.superclass
  end
end

def p_classtree_sub(c)
  array = c.public_instance_methods(false).sort
  
  if (array.size > 5)
    print ""
  else
    print ""
  end
    
  counter = 0
  array.each_with_index do |v, index|
    print v + ", "
    counter += 1
    if (counter >= 5)
      counter = 0
      puts

      if (array.size - index > 5)
        print ""
      else
        print ""
      end
    end
  end
  puts
end

課題

今はasciiコード順にソートしちゃってるけど、本当は'abs'みたいなアルファベット系と、'[]' みたいな演算子系は分けて表示したい所。いい方法無いかなあ?
↓みたいになったら素敵!

Fixnum
|  abs, div, divmod, even?, fdiv, 
|  id2name, modulo, odd?, quo, size, 
|  to_f, to_s, to_sym, zero?, 
|  %, &, *, **, +, -, 
|  -@, /, <, <<, <=, 
|  <=>, ==, >, >=, >>, 
↓  [], ^, |, ~,