1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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
139
140
141
142
143
144
145
146
|
require "mutex_m"
require "debrew/irb"
module Debrew
extend Mutex_m
Ignorable = Module.new
module Raise
def raise(*)
super
rescue Exception => e
e.extend(Ignorable)
super(e) unless Debrew.debug(e) == :ignore
end
alias_method :fail, :raise
end
module Formula
def install
Debrew.debrew { super }
end
def patch
Debrew.debrew { super }
end
def test
Debrew.debrew { super }
end
end
class Menu
Entry = Struct.new(:name, :action)
attr_accessor :prompt, :entries
def initialize
@entries = []
end
def choice(name, &action)
entries << Entry.new(name.to_s, action)
end
def self.choose
menu = new
yield menu
choice = nil
while choice.nil?
menu.entries.each_with_index { |e, i| puts "#{i+1}. #{e.name}" }
print menu.prompt unless menu.prompt.nil?
input = $stdin.gets or exit
input.chomp!
i = input.to_i
if i > 0
choice = menu.entries[i-1]
else
possible = menu.entries.find_all { |e| e.name.start_with?(input) }
case possible.size
when 0 then puts "No such option"
when 1 then choice = possible.first
else puts "Multiple options match: #{possible.map(&:name).join(" ")}"
end
end
end
choice[:action].call
end
end
class << self
alias_method :original_raise, :raise
end
@active = false
@debugged_exceptions = Set.new
def self.active?
@active
end
def self.debugged_exceptions
@debugged_exceptions
end
def self.debrew
@active = true
Object.send(:include, Raise)
begin
yield
rescue SystemExit
original_raise
rescue Exception => e
debug(e)
ensure
@active = false
end
end
def self.debug(e)
original_raise(e) unless active? &&
debugged_exceptions.add?(e) &&
try_lock
begin
puts "#{e.backtrace.first}"
puts "#{Tty.red}#{e.class.name}#{Tty.reset}: #{e}"
loop do
Menu.choose do |menu|
menu.prompt = "Choose an action: "
menu.choice(:raise) { original_raise(e) }
menu.choice(:ignore) { return :ignore } if Ignorable === e
menu.choice(:backtrace) { puts e.backtrace }
menu.choice(:irb) do
puts "When you exit this IRB session, execution will continue."
set_trace_func proc { |event, _, _, id, binding, klass|
if klass == Raise && id == :raise && event == "return"
set_trace_func(nil)
synchronize { IRB.start_within(binding) }
end
}
return :ignore
end if Ignorable === e
menu.choice(:shell) do
puts "When you exit this shell, you will return to the menu."
interactive_shell
end
end
end
ensure
unlock
end
end
end
|