better_errors 0.0.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +3 -0
- data/.yardopts +1 -0
- data/CONTRIBUTING.md +9 -0
- data/README.md +32 -8
- data/better_errors.gemspec +11 -5
- data/lib/better_errors/code_formatter.rb +61 -0
- data/lib/better_errors/core_ext/exception.rb +14 -13
- data/lib/better_errors/disable_logging_middleware.rb +16 -0
- data/lib/better_errors/error_page.rb +72 -48
- data/lib/better_errors/middleware.rb +77 -2
- data/lib/better_errors/rails.rb +7 -4
- data/lib/better_errors/repl/basic.rb +20 -0
- data/lib/better_errors/repl/pry.rb +77 -0
- data/lib/better_errors/repl.rb +30 -0
- data/lib/better_errors/stack_frame.rb +121 -0
- data/lib/better_errors/templates/main.erb +923 -0
- data/lib/better_errors/templates/variable_info.erb +69 -0
- data/lib/better_errors/version.rb +1 -1
- data/lib/better_errors.rb +99 -11
- data/spec/better_errors/code_formatter_spec.rb +51 -0
- data/spec/better_errors/error_page_spec.rb +33 -18
- data/spec/better_errors/middleware_spec.rb +29 -1
- data/spec/better_errors/repl/basic_spec.rb +32 -0
- data/spec/better_errors/stack_frame_spec.rb +121 -0
- data/spec/better_errors_spec.rb +13 -0
- data/spec/spec_helper.rb +3 -0
- metadata +92 -12
- data/lib/better_errors/error_frame.rb +0 -86
- data/lib/better_errors/error_page.erb +0 -234
- data/spec/better_errors/error_frame_spec.rb +0 -61
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
module BetterErrors
|
|
2
|
+
# @private
|
|
3
|
+
class StackFrame
|
|
4
|
+
def self.from_exception(exception)
|
|
5
|
+
idx_offset = 0
|
|
6
|
+
list = exception.backtrace.each_with_index.map do |frame, idx|
|
|
7
|
+
frame_binding = exception.__better_errors_bindings_stack[idx - idx_offset]
|
|
8
|
+
next unless md = /\A(?<file>.*?):(?<line>\d+)(:in `(?<name>.*)')?/.match(frame)
|
|
9
|
+
|
|
10
|
+
# prevent mismatching frames in the backtrace with the binding stack
|
|
11
|
+
if frame_binding and frame_binding.eval("__FILE__") != md[:file]
|
|
12
|
+
idx_offset += 1
|
|
13
|
+
frame_binding = nil
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
StackFrame.new(md[:file], md[:line].to_i, md[:name], frame_binding)
|
|
17
|
+
end.compact
|
|
18
|
+
|
|
19
|
+
if exception.is_a?(SyntaxError) && exception.to_s =~ /\A(.*):(\d*):/
|
|
20
|
+
list.unshift StackFrame.new($1, $2.to_i, "")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
list
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
attr_reader :filename, :line, :name, :frame_binding
|
|
27
|
+
|
|
28
|
+
def initialize(filename, line, name, frame_binding = nil)
|
|
29
|
+
@filename = filename
|
|
30
|
+
@line = line
|
|
31
|
+
@name = name
|
|
32
|
+
@frame_binding = frame_binding
|
|
33
|
+
|
|
34
|
+
set_pretty_method_name if frame_binding
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def application?
|
|
38
|
+
root = BetterErrors.application_root
|
|
39
|
+
filename.index(root) == 0 if root
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def application_path
|
|
43
|
+
filename[(BetterErrors.application_root.length+1)..-1]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def gem?
|
|
47
|
+
Gem.path.any? { |path| filename.index(path) == 0 }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def gem_path
|
|
51
|
+
Gem.path.each do |path|
|
|
52
|
+
if filename.index(path) == 0
|
|
53
|
+
return filename.gsub("#{path}/gems/", "(gem) ")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def class_name
|
|
59
|
+
@class_name
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def method_name
|
|
63
|
+
@method_name || @name
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def context
|
|
67
|
+
if gem?
|
|
68
|
+
:gem
|
|
69
|
+
elsif application?
|
|
70
|
+
:application
|
|
71
|
+
else
|
|
72
|
+
:dunno
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def pretty_path
|
|
77
|
+
case context
|
|
78
|
+
when :application; application_path
|
|
79
|
+
when :gem; gem_path
|
|
80
|
+
else filename
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def local_variables
|
|
85
|
+
return {} unless frame_binding
|
|
86
|
+
frame_binding.eval("local_variables").each_with_object({}) do |name, hash|
|
|
87
|
+
begin
|
|
88
|
+
hash[name] = frame_binding.eval(name.to_s)
|
|
89
|
+
rescue NameError => e
|
|
90
|
+
# local_variables sometimes returns broken variables.
|
|
91
|
+
# https://bugs.ruby-lang.org/issues/7536
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def instance_variables
|
|
97
|
+
return {} unless frame_binding
|
|
98
|
+
Hash[frame_binding.eval("instance_variables").map { |x|
|
|
99
|
+
[x, frame_binding.eval(x.to_s)]
|
|
100
|
+
}]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def to_s
|
|
104
|
+
"#{pretty_path}:#{line}:in `#{name}'"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
def set_pretty_method_name
|
|
109
|
+
name =~ /\A(block (\([^)]+\) )?in )?/
|
|
110
|
+
recv = frame_binding.eval("self")
|
|
111
|
+
return unless method_name = frame_binding.eval("__method__")
|
|
112
|
+
if recv.is_a? Module
|
|
113
|
+
@class_name = "#{$1}#{recv}"
|
|
114
|
+
@method_name = ".#{method_name}"
|
|
115
|
+
else
|
|
116
|
+
@class_name = "#{$1}#{recv.class}"
|
|
117
|
+
@method_name = "##{method_name}"
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|