【Ruby】【メタプログラミング】チェイン(フック)& 特異メソッド を使ったメソッド呼び出しのトレーシング
■topic summary
how to trace method calling with using eigenclass.
委譲とmethod_missingを使ったトレーシングは前にやったけど、
今度はチェイン(フック)を使ったトレーシングを実装します。
ポイント
特異メソッドを使ったトレーシング
・式展開が2レベルに渡る(特異メソッド定義時、実行時)
・インスタンス変数は不要になった時点で削除(@_traced)
・evalで定義するメソッドが、
⇒引数にブロックを持つ場合・・・evalの引数は文字列
⇒ブロックを持たない場合・・・evalの引数はブロックでOK
・特異クラス生成イディオム(1個め、2個めのselfはそれぞれ別オブジェクトを参照)
コード
class Object def trace!(*methods) @_traced = @_traced || [] methods = public_methods(false) if methods.size == 0 methods.map!{|m| m.intern} methods -= @_traced return if methods.empty? @_traced |= methods eigenclass = class << self; self; end STDOUT << "Tracing #{methods.join(",")} on #{object_id}\n" methods.each do |m| eigenclass.class_eval %Q{ def #{m}(*args, &b) STDOUT << "Entering #{m}(\#{args.join(",")})\n" result = super || "nil" STDOUT << "Exiting #{m} with \#{result}\n" result end } end end def untrace!(*methods) if methods.size == 0 methods = @_traced else methods &= @_traced end STDOUT << "Untracing #{methods.join(",")} on #{object_id}\n" methods.map!{|m| m.intern} eigenclass = class << self; self; end eigenclass.class_eval do methods.each do |m| remove_method m end end @_traced -= methods end end b = [1,2,3] b.trace!(:index) b.index("a") b.untrace!(:index) b.index("a")
■実行結果
Tracing index on 22638228 Entering index(a) Exiting index with nil Untracing index on 22638228
ふむふむ。