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.
@@ -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