Skip to content

irb method, class, and definition capturing

rocky edited this page Sep 13, 2010 · 1 revision

Here’s something cool using trace that I’ll make use of in the Ruby 1.9 debugger.

I want to be able to debug and trace methods I type into irb and see nice source listings.

I think this can be done in Ruby 1.9 without this trace module, but there are two advantages to using the trace module.

The biggest reason is that the trace module allows for multiple event hooks. So you can install this trace hook and debug which installs another (and a profiler which might install a 3rd). The second advantage is that this is faster since the event checking is done inside interpreter.

Below you’ll see that when I get the definition for method “foo” it contains entire string for the class it is in A. But this is exactly what I want! That is I don’t want the string for just “foo”. This is because what’s important is to synchronize the line numbers that the VM evaluator may spit out on an exception with the source text, here a string.

Here is a sample trial.

 $ irb
 >> load '/home/rocky/irbplus/irb-hack.rb'
 >> class A
 >>   def foo
 >>     5
 >>   end
 >> end
 => nil
 >> puts CLASSES['A']
 class A
   def foo
     5
   end
 end
 => nil
 >> puts METHODS['foo']
 class A
   def foo
     5
   end
 end
 => nil
 >> module M
 >>   CONSTANT=5
 >> end
 => 5
 >> puts MODULES['M']
 module M
   CONSTANT=5
 end
 => nil
 >> 
Here is the code
 require 'irb'
 require 'trace'
 
 include Trace
 Trace.event_masks = C_CALL_EVENT_MASK
 trace_filter = TraceFilter.new
 CLASSES = {}
 METHODS = {}
 MODULES = {}
 
 def capture_hook(event, frame, arg=nil)
   methods = %w(define_method method_added CLASS)
   return unless methods.member?(frame.method)
   METHODS[$1] = $irb_stmts if $irb_stmts =~  /^\s*def\s+(\S+)(:?\(|\n)/
   CLASSES[$1] = $irb_stmts if $irb_stmts =~ /^\s*class\s+(\S+)\s+/
   MODULES[$1] = $irb_stmts if $irb_stmts =~ /^\s*module\s+(\S+)\s+/
 end
 
 # Monkeypatch to save the current IRB statement to be run.
 # Possibly not needed.
 class IRB::Context
   alias original_evaluate evaluate
   def evaluate(line, line_no)
     $irb_stmts = line
     original_evaluate(line, line_no)
   end
 end
 
 workspace = IRB::WorkSpace.new(binding)

 irb = IRB::Irb.new(workspace)
 trace_filter.set_trace_func(method(:capture_hook).to_proc)
 irb.eval_input
Clone this wiki locally