せかいや

いまいるここを、おもしろく http://sekai-in-the-box.appspot.com/

【Ruby】【メタプログラミング】callerメソッド。method_missingを使ったメソッド呼び出しのトレーシング

 
 
■topic summary
study about meta-programming with Ruby.

 

プログラミング言語 Ruby

プログラミング言語 Ruby

この本で、最後に大切に残していた、メタプログラミングの章を勉強するよ。
分かるかな。。


 

callerメソッド

引数なしの場合は、スタックフレームが1個省略された形になる

class Hoge
  def meth1
    meth2
  end
  def meth2
    p caller
    p caller(0)
  end
end
Hoge.new.meth1

 
■実行結果

["C:/Users/Hoge_ADMIN/Desktop/test/fizzbazz.rb:4:in `meth1'", "C:/Users/Hoge_ADMIN/Desktop/test/fizzbazz.rb:12:in `<main>'"]
["C:/Users/Hoge_ADMIN/Desktop/test/fizzbazz.rb:8:in `meth2'", "C:/Users/Hoge_ADMIN/Desktop/test/fizzbazz.rb:4:in `meth1'", "C:/Users/Hoge_ADMIN/Desktop/test/fizzbazz.rb:12:in `<main>'"]

 
なるほど。

 

method_missingを使ったメソッド呼び出しのトレーシング

class Object
  def trace(name, stream=STDOUT)
    return TraceObject.new(self, name, stream)
  end
end
class TraceObject
  #TraceObjectの既存インスタンスメソッドを消す
  instance_methods.each do |m|
    next if m == :inspect || m == :object_id ||  m == :__id__ || m == :__send__
    undef_method m
  end
  def initialize(o, name, stream)
    @o, @n, @trace = o, name, stream
  end
  def method_missing(*args, &b)
    p method_name = args.shift
    arglist = args.map{|ar| ar.inspect}.join(",")
    @trace << "Invoking: #{@n}.#{method_name}(#{arglist}) called at #{caller[0]}\r\n"
    begin
      result = @o.send(method_name, *args, &b)
      @trace << "Returning: #{result} from #{@n}.#{method_name}(#{arglist}) called at #{caller[0]}\r\n"
      result
    rescue Exception=>e
      @trace << "Raising: #{e.class}:#{e} from #{@n}.#{method_name}(#{arglist})}\r\n"
      raise
    end
  end
  def __delegate
    @o
  end
end
a = [1,2,3].trace("target")
p a  #<= TraceObjectオブジェクトは出力できない
p a.__delegate
a.<<(4)
a.fetch(10)

 
■実行結果

Invoking: target.inspect() called at fizzbazz.rb:34:in `p'
Returning: [1, 2, 3] from target.inspect() called at fizzbazz.rb:34:in `p'
[1, 2, 3]  #<= 委譲元オブジェクトが出力される
[1, 2, 3]
Invoking: target.<<(4) called at fizzbazz.rb:36:in `<main>'
Returning: [1, 2, 3, 4] from target.<<(4) called at fizzbazz.rb:36:in `<main>'
Invoking: target.fetch(10) called at fizzbazz.rb:37:in `<main>'
Raising: IndexError:index 10 outside of array bounds: -4...4 from target.fetch(10)}


 

注意(?)

委譲先オブジェクトを出力したいときは、
pメソッド内で実行されるinspectメソッドを消去対象からはずす。

next if m == :inspect || m == :object_id ||  m == :__id__ || m == :__send__

 
■実行結果

#<TraceObject:0x2e3e138 @o=[1, 2, 3], @n="target", @trace=#<IO:<STDOUT>>>
[1, 2, 3]


 
ふむふむ。