forked from BetterErrors/better_errors
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstack_frame.rb
More file actions
130 lines (106 loc) · 3.21 KB
/
stack_frame.rb
File metadata and controls
130 lines (106 loc) · 3.21 KB
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
require "set"
module BetterErrors
# @private
class StackFrame
def self.from_exception(exception)
RaisedException.new(exception).backtrace
end
attr_reader :filename, :line, :name, :frame_binding
def initialize(filename, line, name, frame_binding = nil)
@filename = filename
@line = line
@name = name
@frame_binding = frame_binding
set_pretty_method_name if frame_binding
end
def application?
if root = BetterErrors.application_root
filename.index(root) == 0 && filename.index("#{root}/vendor") != 0
end
end
def application_path
filename[(BetterErrors.application_root.length+1)..-1]
end
def gem?
Gem.path.any? { |path| filename.index(path) == 0 }
end
def gem_path
if path = Gem.path.detect { |p| filename.index(p) == 0 }
gem_name_and_version, path = filename.sub("#{path}/gems/", "").split("/", 2)
/(?<gem_name>.+)-(?<gem_version>[\w.]+)/ =~ gem_name_and_version
"#{gem_name} (#{gem_version}) #{path}"
end
end
def class_name
@class_name
end
def method_name
@method_name || @name
end
def context
if gem?
:gem
elsif application?
:application
else
:dunno
end
end
def pretty_path
case context
when :application; application_path
when :gem; gem_path
else filename
end
end
def local_variables
return {} unless frame_binding
frame_binding.eval("local_variables").each_with_object({}) do |name, hash|
# Ruby 2.2's local_variables will include the hidden #$! variable if
# called from within a rescue context. This is not a valid variable name,
# so the local_variable_get method complains. This should probably be
# considered a bug in Ruby itself, but we need to work around it.
next if name == :"\#$!"
hash[name] = local_variable(name)
end
end
def local_variable(name)
get_local_variable(name) || eval_local_variable(name)
rescue NameError => ex
"#{ex.class}: #{ex.message}"
end
def instance_variables
return {} unless frame_binding
Hash[visible_instance_variables.map { |x|
[x, frame_binding.eval(x.to_s)]
}]
end
def visible_instance_variables
frame_binding.eval("instance_variables") - BetterErrors.ignored_instance_variables
end
def to_s
"#{pretty_path}:#{line}:in `#{name}'"
end
private
def set_pretty_method_name
name =~ /\A(block (\([^)]+\) )?in )?/
recv = frame_binding.eval("self")
return unless method_name = frame_binding.eval("::Kernel.__method__")
if Module === recv
@class_name = "#{$1}#{recv}"
@method_name = ".#{method_name}"
else
@class_name = "#{$1}#{Kernel.instance_method(:class).bind(recv).call}"
@method_name = "##{method_name}"
end
end
def get_local_variable(name)
if defined?(frame_binding.local_variable_get)
frame_binding.local_variable_get(name)
end
end
def eval_local_variable(name)
frame_binding.eval(name.to_s)
end
end
end