Ruby Debug Injection

Rubyでプログラムをよく書くのですが、メソッドの入出力を監視したい、つまりメソッドの引数と返り値が何であるか知りたくなることがあります。
こんなとき、いちいち当該メソッドにいわゆるprintfを仕込んだりするのは面倒なので、何かよい案がないか考えてみました。もしかしたら標準の機能でそういうものがあったかもしれませんが、とりあえず作ってみたので公開してみたいと思います。

module Debugger
    def debug(target_method, do_debug = true)
        if target_method.class != Symbol then
            target_method = target_method.intern
        end
        target_orig = "#{target_method}_orig".intern
        unless self.instance_methods.include?(target_orig.to_s) then
            self.instance_eval{
                alias_method(target_orig, target_method)
            }
        end
        target_debug = "#{target_method}_debug".intern
        unless self.instance_methods.include?(target_debug.to_s) then
            self.module_eval(<<-__EVAL_STRING__)
                def #{target_debug}(*params)
                    params.each_with_index{|param, i|
                        puts 'INPUT' + i.to_s + ': ' + param.inspect
                    }
                    output = #{target_orig}(*params)
                    puts 'OUTPUT: ' + output.inspect
                    return output
                end
            __EVAL_STRING__
        end
        self.instance_eval{
            alias_method(target_method, do_debug ? target_debug : target_orig)
        }
    end
end
使い方としては
class Target
    def a(b, c=nil)
        puts "ARG0: #{b}"
        puts "ARG1: #{c}"
        return c
    end
end

class Target
    extend Debugger
end

Target::new.a(1)    # normal
Target::new.a(1,2)    # normal

Target::debug(:a)    #attach debugger to method :a
Target::new.a(1)    # debugging ...
Target::new.a(1,2)    # debugging ...

Target::debug(:a, false)    #detach debugger from method :a
Target::new.a(1)    # normal
Target::new.a(1,2)    # normal

スクリプト言語だから当たり前といってしまえばそれまでですが、Rubyはリフレクションの機能が強力なので怪しげなことが簡単にできてしまって楽しいです。でも、リフレクションを使いまくると明らかにコードが読みにくくなり、他の人に迷惑がかかるので注意したほうがいいかもしれません。ちなみに僕はリフレクションは好きな方なので、チームで開発した際に他のメンバーに迷惑をかけた記憶があります(笑)。

July 07, 2006 23:59 fenrir が投稿 : 固定リンク | | このエントリーを含むはてなブックマーク

コメント

コメントする