class Kung
  mstr = %-
        def foo
          puts 'Hello World from Kung.foo'
          puts caller(0)
        end
      -
  module_eval mstr
end
    
Kung.new.foo
Which generates the following output:
Hello World from Kung.foo
(eval):4:in `foo'
Kung-foo.rb:11
The stack trace only shows "(Eval):4:in 'foo'" which is almost useless. The "(Eval)" is a clue that the method was dynamically created using meta-programming. In this simple example, it is easy to find the dynamic code since it is near the caller "Kung-foo.rb:11". However in a real project it is frequently located far away, possibly in other source files.
To fix the stack trace, the author should use the optional arguments to method_eval as follows:
class Monkey
  line, mstr = __LINE__, %-
        def see
          puts 'Hello World from Monkey.see'
          puts caller(0)
        end
      -
  module_eval mstr, __FILE__, line
end
    
Monkey.new.see
The output now shows the correct line number and file name:
Hello World from Monkey.see
Monkey-see.rb:5:in `see'
Monkey-see.rb:11
Update after reading the code in ActiveSupport core_ext\attribute_accessors.rb I found a nice way to do the above with fewer lines of code:
class Monkey
  module_eval(<<-EOS, __FILE__, __LINE__)
        def see
          puts 'Hello World from Monkey.see'
          puts caller(0)
        end
      EOS
end
    
Monkey.new.see
 
 
No comments:
Post a Comment