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
使い方としては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
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はリフレクションの機能が強力なので怪しげなことが簡単にできてしまって楽しいです。でも、リフレクションを使いまくると明らかにコードが読みにくくなり、他の人に迷惑がかかるので注意したほうがいいかもしれません。ちなみに僕はリフレクションは好きな方なので、チームで開発した際に他のメンバーに迷惑をかけた記憶があります(笑)。
コメント
コメントする
- 匿名でのコメントは受け付けておりません。
- お名前(ハンドル名可)とメールアドレスは必ず入力してください。
- メールアドレスを表示されたくないときはURLも必ず記入してください。
- コメント欄でHTMLタグは使用できません。
- コメント本文に日本語(全角文字)がある程度多く含まれている必要があります。
- コメント欄内のURLと思われる文字列は自動的にリンクに変換されます。
- 投稿ボタンを押してエラーがでなければ、投稿は成功しています。反映されるまでには少し時間がかかります。