Module: Dexc

Defined in:
lib/dexc.rb,
lib/dexc/version.rb

Defined Under Namespace

Modules: IrbHelper Classes: RaiseEvent, ReturnEvent, RingBuffer

Constant Summary collapse

EXC_CALLERS_VAR =
:@dexc_callers
VERSION =
"0.1.0"

Class Method Summary collapse

Class Method Details

.show_line(path, lineno, index, index_width, cache) ⇒ Object



152
153
154
155
156
157
158
159
160
161
# File 'lib/dexc.rb', line 152

def show_line(path, lineno, index, index_width, cache)
  print "#{"%#{index_width}d" % index}:#{path}:#{lineno}"
  begin
    cache[path] ||= open(path).each_line.map(&:chomp)
    print "> #{lineno > 0 ? ::IRB::Color.colorize_code(cache[path][lineno - 1], complete: false, ignore_error: true) : ''}"
  rescue Errno::ENOENT
    cache[path] = []
  end
  puts
end

.show_trace(events) ⇒ Object



141
142
143
144
145
146
147
148
149
# File 'lib/dexc.rb', line 141

def show_trace(events)
  idx_width = (events.length - 1).to_s.length
  file_cache = {}
  events.each_with_index do |i, idx|
    show_line(i.path, i.lineno, idx, idx_width, file_cache)
    puts " " * (idx_width + 1) + "#{i.defined_class}##{i.method_id}#{i.event == :b_return ? '(block)' : ''}: #{IRB::ColorPrinter.pp(i.return_value.inspect, '')}"
  end
  puts
end

.startObject



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/dexc.rb', line 75

def start
  events = RingBuffer.new(30)

  tp = TracePoint.new(:raise, :return, :c_return, :b_return) do |tp|
    if tp.event == :raise
      exc = tp.raised_exception
      exc.instance_variable_set(EXC_CALLERS_VAR, tp.binding.callers[1..-1])
      events.add(RaiseEvent.new(tp.event, exc))
    else
      events.add(ReturnEvent.new(tp.event, tp.lineno, tp.path, tp.defined_class, tp.method_id, tp.return_value))
    end
  end

  at_exit do
    exc = $!
    tp.disable
    callers = exc.instance_variable_get(EXC_CALLERS_VAR)
    if exc.kind_of?(StandardError) and callers
      raise_idx = events.to_a.find_index {|i| i.event == :raise and i.raised_exception == exc }
      latest_events = raise_idx ? events.to_a[0...raise_idx] : []
      return_events = latest_events.find_all {|i| i.event != :raise }
      return_values = return_events.map(&:return_value)

      show_trace(return_events)
      puts exc.full_message

      Kernel.module_eval do
        define_method(:dexc_hist) do
          return_values
        end
        alias_method :hist, :dexc_hist
      end

      begin
        require 'pry'
        Pry.config.hooks.add_hook(:when_started, :dexc_init_ex) do |_, _, pry|
          pry.last_exception = exc
          pry.backtrace = (exc.backtrace || [])
        end
        callers[0].pry
      rescue LoadError
        require 'irb'
        IRB::Context.include(IrbHelper)
        require 'dexc/irb/cmd/stack_explorer'
        b = callers[0]
        filename = b.source_location[0]
        IRB.setup(filename, argv: [])
        workspace = IRB::WorkSpace.new(b)
        STDOUT.print(workspace.code_around_binding)
        binding_irb = IRB::Irb.new(workspace)
        binding_irb.context.irb_path = File.expand_path(filename)
        binding_irb.context.instance_eval do
          @dexc_callers = callers
          @dexc_callers_width = Math.log10(callers.length).floor + 1
          @dexc_callers_idx = 0
        end
        binding_irb.run(IRB.conf)
      end
      exit!
    end
  end

  tp.enable
end