/
error_page.rb
151 lines (119 loc) · 3.96 KB
/
error_page.rb
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
147
148
149
150
151
require "cgi"
require "json"
require "securerandom"
module BetterErrors
# @private
class ErrorPage
def self.template_path(template_name)
File.expand_path("../templates/#{template_name}.erb", __FILE__)
end
def self.template(template_name)
Erubi::Engine.new(File.read(template_path(template_name)), escape: true)
end
attr_reader :exception, :env, :repls
def initialize(exception, env)
@exception = RaisedException.new(exception)
@env = env
@start_time = Time.now.to_f
@repls = []
end
def id
@id ||= SecureRandom.hex(8)
end
def render(template_name = "main", csrf_token = nil)
binding.eval(self.class.template(template_name).src)
rescue => e
# Fix the backtrace, which doesn't identify the template that failed (within Better Errors).
# We don't know the line number, so just injecting the template path has to be enough.
e.backtrace.unshift "#{self.class.template_path(template_name)}:0"
raise
end
def do_variables(opts)
index = opts["index"].to_i
@frame = backtrace_frames[index]
@var_start_time = Time.now.to_f
{ html: render("variable_info") }
end
def do_eval(opts)
index = opts["index"].to_i
code = opts["source"]
unless (binding = backtrace_frames[index].frame_binding)
return { error: "REPL unavailable in this stack frame" }
end
@repls[index] ||= REPL.provider.new(binding, exception)
eval_and_respond(index, code)
end
def backtrace_frames
exception.backtrace
end
def exception_type
exception.type
end
def exception_message
exception.message.strip.gsub(/(\r?\n\s*\r?\n)+/, "\n")
end
def active_support_actions
return [] unless defined?(ActiveSupport::ActionableError)
ActiveSupport::ActionableError.actions(exception.type)
end
def action_dispatch_action_endpoint
return unless defined?(ActionDispatch::ActionableExceptions)
ActionDispatch::ActionableExceptions.endpoint
end
def application_frames
backtrace_frames.select(&:application?)
end
def first_frame
application_frames.first || backtrace_frames.first
end
private
def editor_url(frame)
BetterErrors.editor[frame.filename, frame.line]
end
def rack_session
env['rack.session']
end
def rails_params
env['action_dispatch.request.parameters']
end
def uri_prefix
env["SCRIPT_NAME"] || ""
end
def request_path
env["PATH_INFO"]
end
def html_formatted_code_block(frame)
CodeFormatter::HTML.new(frame.filename, frame.line).output
end
def text_formatted_code_block(frame)
CodeFormatter::Text.new(frame.filename, frame.line).output
end
def text_heading(char, str)
str + "\n" + char*str.size
end
def inspect_value(obj)
if BetterErrors.ignored_classes.include? obj.class.name
"<span class='unsupported'>(Instance of ignored class. "\
"#{obj.class.name ? "Remove #{CGI.escapeHTML(obj.class.name)} from" : "Modify"}"\
" BetterErrors.ignored_classes if you need to see it.)</span>"
else
InspectableValue.new(obj).to_html
end
rescue BetterErrors::ValueLargerThanConfiguredMaximum
"<span class='unsupported'>(Object too large. "\
"#{obj.class.name ? "Modify #{CGI.escapeHTML(obj.class.name)}#inspect or a" : "A"}"\
"djust BetterErrors.maximum_variable_inspect_size if you need to see it.)</span>"
rescue Exception => e
"<span class='unsupported'>(exception #{CGI.escapeHTML(e.class.to_s)} was raised in inspect)</span>"
end
def eval_and_respond(index, code)
result, prompt, prefilled_input = @repls[index].send_input(code)
{
highlighted_input: CodeRay.scan(code, :ruby).div(wrap: nil),
prefilled_input: prefilled_input,
prompt: prompt,
result: result
}
end
end
end