better_errors 2.8.2 → 2.10.0.beta2
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +142 -0
- data/.github/workflows/release.yml +68 -0
- data/.gitignore +4 -0
- data/.ruby-version +1 -0
- data/Gemfile +6 -1
- data/README.md +2 -2
- data/better_errors.gemspec +5 -4
- data/gemfiles/pry010.gemfile +2 -1
- data/gemfiles/pry011.gemfile +2 -1
- data/gemfiles/pry09.gemfile +2 -1
- data/gemfiles/rack.gemfile +2 -1
- data/gemfiles/rack_boc.gemfile +2 -1
- data/gemfiles/rails42.gemfile +2 -1
- data/gemfiles/rails42_boc.gemfile +2 -1
- data/gemfiles/rails42_haml.gemfile +2 -1
- data/gemfiles/rails50.gemfile +2 -1
- data/gemfiles/rails50_boc.gemfile +2 -1
- data/gemfiles/rails50_haml.gemfile +2 -1
- data/gemfiles/rails51.gemfile +2 -1
- data/gemfiles/rails51_boc.gemfile +2 -1
- data/gemfiles/rails51_haml.gemfile +2 -1
- data/gemfiles/rails52.gemfile +2 -1
- data/gemfiles/rails52_boc.gemfile +2 -1
- data/gemfiles/rails52_haml.gemfile +2 -1
- data/gemfiles/rails60.gemfile +2 -1
- data/gemfiles/rails60_boc.gemfile +2 -1
- data/gemfiles/rails60_haml.gemfile +2 -1
- data/gemfiles/rails61.gemfile +8 -0
- data/gemfiles/rails61_boc.gemfile +9 -0
- data/gemfiles/rails61_haml.gemfile +9 -0
- data/lib/better_errors.rb +15 -35
- data/lib/better_errors/code_formatter.rb +16 -27
- data/lib/better_errors/code_formatter/html.rb +15 -1
- data/lib/better_errors/editor.rb +103 -0
- data/lib/better_errors/error_page.rb +33 -15
- data/lib/better_errors/error_page_style.rb +30 -0
- data/lib/better_errors/exception_hint.rb +29 -0
- data/lib/better_errors/middleware.rb +20 -4
- data/lib/better_errors/raised_exception.rb +8 -1
- data/lib/better_errors/templates/main.erb +80 -717
- data/lib/better_errors/templates/text.erb +6 -3
- data/lib/better_errors/templates/variable_info.erb +18 -15
- data/lib/better_errors/version.rb +2 -1
- metadata +30 -7
- data/.travis.yml +0 -111
@@ -0,0 +1,30 @@
|
|
1
|
+
require "sassc"
|
2
|
+
|
3
|
+
module BetterErrors
|
4
|
+
# @private
|
5
|
+
module ErrorPageStyle
|
6
|
+
def self.compiled_css(for_deployment = false)
|
7
|
+
style_dir = File.expand_path("style", File.dirname(__FILE__))
|
8
|
+
style_file = "#{style_dir}/main.scss"
|
9
|
+
|
10
|
+
engine = SassC::Engine.new(
|
11
|
+
File.read(style_file),
|
12
|
+
filename: style_file,
|
13
|
+
style: for_deployment ? :compressed : :expanded,
|
14
|
+
line_comments: !for_deployment,
|
15
|
+
load_paths: [style_dir],
|
16
|
+
)
|
17
|
+
engine.render
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.style_tag(csp_nonce)
|
21
|
+
style_file = File.expand_path("templates/main.css", File.dirname(__FILE__))
|
22
|
+
css = if File.exist?(style_file)
|
23
|
+
File.open(style_file).read
|
24
|
+
else
|
25
|
+
compiled_css(false)
|
26
|
+
end
|
27
|
+
"<style type='text/css' nonce='#{csp_nonce}'>\n#{css}\n</style>"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module BetterErrors
|
2
|
+
class ExceptionHint
|
3
|
+
def initialize(exception)
|
4
|
+
@exception = exception
|
5
|
+
end
|
6
|
+
|
7
|
+
def hint
|
8
|
+
case exception
|
9
|
+
when NoMethodError
|
10
|
+
/\Aundefined method `(?<method>[^']+)' for (?<val>[^:]+):(?<klass>\w+)/.match(exception.message) do |match|
|
11
|
+
if match[:val] == "nil"
|
12
|
+
return "Something is `nil` when it probably shouldn't be."
|
13
|
+
elsif !match[:klass].start_with? '0x'
|
14
|
+
return "`#{match[:method]}` is being called on a `#{match[:klass]}` object, "\
|
15
|
+
"which might not be the type of object you were expecting."
|
16
|
+
end
|
17
|
+
end
|
18
|
+
when NameError
|
19
|
+
/\Aundefined local variable or method `(?<method>[^']+)' for/.match(exception.message) do |match|
|
20
|
+
return "`#{match[:method]}` is probably misspelled."
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :exception
|
28
|
+
end
|
29
|
+
end
|
@@ -40,7 +40,7 @@ module BetterErrors
|
|
40
40
|
allow_ip! "127.0.0.0/8"
|
41
41
|
allow_ip! "::1/128" rescue nil # windows ruby doesn't have ipv6 support
|
42
42
|
|
43
|
-
CSRF_TOKEN_COOKIE_NAME = "BetterErrors-#{VERSION}-CSRF-Token"
|
43
|
+
CSRF_TOKEN_COOKIE_NAME = "BetterErrors-#{BetterErrors::VERSION}-CSRF-Token"
|
44
44
|
|
45
45
|
# A new instance of BetterErrors::Middleware
|
46
46
|
#
|
@@ -94,12 +94,13 @@ module BetterErrors
|
|
94
94
|
def show_error_page(env, exception=nil)
|
95
95
|
request = Rack::Request.new(env)
|
96
96
|
csrf_token = request.cookies[CSRF_TOKEN_COOKIE_NAME] || SecureRandom.uuid
|
97
|
+
csp_nonce = SecureRandom.base64(12)
|
97
98
|
|
98
99
|
type, content = if @error_page
|
99
100
|
if text?(env)
|
100
|
-
[ 'plain', @error_page.
|
101
|
+
[ 'plain', @error_page.render_text ]
|
101
102
|
else
|
102
|
-
[ 'html', @error_page.
|
103
|
+
[ 'html', @error_page.render_main(csrf_token, csp_nonce) ]
|
103
104
|
end
|
104
105
|
else
|
105
106
|
[ 'html', no_errors_page ]
|
@@ -110,7 +111,22 @@ module BetterErrors
|
|
110
111
|
status_code = ActionDispatch::ExceptionWrapper.new(env, exception).status_code
|
111
112
|
end
|
112
113
|
|
113
|
-
|
114
|
+
headers = {
|
115
|
+
"Content-Type" => "text/#{type}; charset=utf-8",
|
116
|
+
"Content-Security-Policy" => [
|
117
|
+
"default-src 'none'",
|
118
|
+
# Specifying nonce makes a modern browser ignore 'unsafe-inline' which could still be set
|
119
|
+
# for older browsers without nonce support.
|
120
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src
|
121
|
+
"script-src 'self' 'nonce-#{csp_nonce}' 'unsafe-inline'",
|
122
|
+
"style-src 'self' 'nonce-#{csp_nonce}' 'unsafe-inline'",
|
123
|
+
"img-src data:",
|
124
|
+
"connect-src 'self'",
|
125
|
+
"navigate-to 'self' #{BetterErrors.editor.scheme}",
|
126
|
+
].join('; '),
|
127
|
+
}
|
128
|
+
|
129
|
+
response = Rack::Response.new(content, status_code, headers)
|
114
130
|
|
115
131
|
unless request.cookies[CSRF_TOKEN_COOKIE_NAME]
|
116
132
|
response.set_cookie(CSRF_TOKEN_COOKIE_NAME, value: csrf_token, path: "/", httponly: true, same_site: :strict)
|
@@ -1,7 +1,9 @@
|
|
1
|
+
require 'better_errors/exception_hint'
|
2
|
+
|
1
3
|
# @private
|
2
4
|
module BetterErrors
|
3
5
|
class RaisedException
|
4
|
-
attr_reader :exception, :message, :backtrace
|
6
|
+
attr_reader :exception, :message, :backtrace, :hint
|
5
7
|
|
6
8
|
def initialize(exception)
|
7
9
|
if exception.class.name == "ActionView::Template::Error" && exception.respond_to?(:cause)
|
@@ -23,6 +25,7 @@ module BetterErrors
|
|
23
25
|
@message = exception.message
|
24
26
|
|
25
27
|
setup_backtrace
|
28
|
+
setup_hint
|
26
29
|
massage_syntax_error
|
27
30
|
end
|
28
31
|
|
@@ -78,5 +81,9 @@ module BetterErrors
|
|
78
81
|
end
|
79
82
|
end
|
80
83
|
end
|
84
|
+
|
85
|
+
def setup_hint
|
86
|
+
@hint = ExceptionHint.new(exception).hint
|
87
|
+
end
|
81
88
|
end
|
82
89
|
end
|
@@ -2,714 +2,14 @@
|
|
2
2
|
<html>
|
3
3
|
<head>
|
4
4
|
<title><%= exception_type %> at <%= request_path %></title>
|
5
|
+
<link rel="icon" href="data:;base64,=" />
|
5
6
|
</head>
|
6
|
-
<body>
|
7
|
+
<body class="better-errors-javascript-not-loaded">
|
7
8
|
<%# Stylesheets are placed in the <body> for Turbolinks compatibility. %>
|
8
|
-
|
9
|
-
/* Basic reset */
|
10
|
-
* {
|
11
|
-
margin: 0;
|
12
|
-
padding: 0;
|
13
|
-
}
|
14
|
-
|
15
|
-
table {
|
16
|
-
width: 100%;
|
17
|
-
border-collapse: collapse;
|
18
|
-
}
|
19
|
-
|
20
|
-
th, td {
|
21
|
-
vertical-align: top;
|
22
|
-
text-align: left;
|
23
|
-
}
|
24
|
-
|
25
|
-
textarea {
|
26
|
-
resize: none;
|
27
|
-
}
|
28
|
-
|
29
|
-
body {
|
30
|
-
font-size: 10pt;
|
31
|
-
}
|
32
|
-
|
33
|
-
body, td, input, textarea {
|
34
|
-
font-family: helvetica neue, lucida grande, sans-serif;
|
35
|
-
line-height: 1.5;
|
36
|
-
color: #333;
|
37
|
-
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
|
38
|
-
}
|
39
|
-
|
40
|
-
html {
|
41
|
-
background: #f0f0f5;
|
42
|
-
}
|
43
|
-
|
44
|
-
.clearfix::after{
|
45
|
-
clear: both;
|
46
|
-
content: ".";
|
47
|
-
display: block;
|
48
|
-
height: 0;
|
49
|
-
visibility: hidden;
|
50
|
-
}
|
51
|
-
|
52
|
-
/* ---------------------------------------------------------------------
|
53
|
-
* Basic layout
|
54
|
-
* --------------------------------------------------------------------- */
|
55
|
-
|
56
|
-
/* Small */
|
57
|
-
@media screen and (max-width: 1100px) {
|
58
|
-
html {
|
59
|
-
overflow-y: scroll;
|
60
|
-
}
|
61
|
-
|
62
|
-
body {
|
63
|
-
margin: 0 20px;
|
64
|
-
}
|
65
|
-
|
66
|
-
header.exception {
|
67
|
-
margin: 0 -20px;
|
68
|
-
}
|
69
|
-
|
70
|
-
nav.sidebar {
|
71
|
-
padding: 0;
|
72
|
-
margin: 20px 0;
|
73
|
-
}
|
74
|
-
|
75
|
-
ul.frames {
|
76
|
-
max-height: 200px;
|
77
|
-
overflow: auto;
|
78
|
-
}
|
79
|
-
}
|
80
|
-
|
81
|
-
/* Wide */
|
82
|
-
@media screen and (min-width: 1100px) {
|
83
|
-
header.exception {
|
84
|
-
position: fixed;
|
85
|
-
top: 0;
|
86
|
-
left: 0;
|
87
|
-
right: 0;
|
88
|
-
}
|
89
|
-
|
90
|
-
nav.sidebar,
|
91
|
-
.frame_info {
|
92
|
-
position: fixed;
|
93
|
-
top: 95px;
|
94
|
-
bottom: 0;
|
95
|
-
|
96
|
-
box-sizing: border-box;
|
97
|
-
|
98
|
-
overflow-y: auto;
|
99
|
-
overflow-x: hidden;
|
100
|
-
}
|
101
|
-
|
102
|
-
nav.sidebar {
|
103
|
-
width: 40%;
|
104
|
-
left: 20px;
|
105
|
-
top: 115px;
|
106
|
-
bottom: 20px;
|
107
|
-
}
|
108
|
-
|
109
|
-
.frame_info {
|
110
|
-
right: 0;
|
111
|
-
left: 40%;
|
112
|
-
|
113
|
-
padding: 20px;
|
114
|
-
padding-left: 10px;
|
115
|
-
margin-left: 30px;
|
116
|
-
}
|
117
|
-
}
|
118
|
-
|
119
|
-
nav.sidebar {
|
120
|
-
background: #d3d3da;
|
121
|
-
border-top: solid 3px #a33;
|
122
|
-
border-bottom: solid 3px #a33;
|
123
|
-
border-radius: 4px;
|
124
|
-
box-shadow: 0 0 6px rgba(0, 0, 0, 0.2), inset 0 0 0 1px rgba(0, 0, 0, 0.1);
|
125
|
-
}
|
126
|
-
|
127
|
-
/* ---------------------------------------------------------------------
|
128
|
-
* Header
|
129
|
-
* --------------------------------------------------------------------- */
|
130
|
-
|
131
|
-
header.exception {
|
132
|
-
padding: 18px 20px;
|
133
|
-
|
134
|
-
height: 59px;
|
135
|
-
min-height: 59px;
|
136
|
-
|
137
|
-
overflow: hidden;
|
138
|
-
|
139
|
-
background-color: #20202a;
|
140
|
-
color: #aaa;
|
141
|
-
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.3);
|
142
|
-
font-weight: 200;
|
143
|
-
box-shadow: inset 0 -5px 3px -3px rgba(0, 0, 0, 0.05), inset 0 -1px 0 rgba(0, 0, 0, 0.05);
|
144
|
-
|
145
|
-
-webkit-text-smoothing: antialiased;
|
146
|
-
}
|
147
|
-
|
148
|
-
/* Heading */
|
149
|
-
header.exception .fix-actions {
|
150
|
-
margin-top: .5em;
|
151
|
-
}
|
152
|
-
|
153
|
-
header.exception .fix-actions input[type=submit] {
|
154
|
-
font-weight: bold;
|
155
|
-
}
|
156
|
-
|
157
|
-
header.exception h2 {
|
158
|
-
font-weight: 200;
|
159
|
-
font-size: 11pt;
|
160
|
-
}
|
161
|
-
|
162
|
-
header.exception h2,
|
163
|
-
header.exception p {
|
164
|
-
line-height: 1.5em;
|
165
|
-
overflow: hidden;
|
166
|
-
white-space: pre;
|
167
|
-
text-overflow: ellipsis;
|
168
|
-
}
|
169
|
-
|
170
|
-
header.exception h2 strong {
|
171
|
-
font-weight: 700;
|
172
|
-
color: #d55;
|
173
|
-
}
|
174
|
-
|
175
|
-
header.exception p {
|
176
|
-
font-weight: 200;
|
177
|
-
font-size: 17pt;
|
178
|
-
color: white;
|
179
|
-
}
|
180
|
-
|
181
|
-
header.exception:hover {
|
182
|
-
height: auto;
|
183
|
-
z-index: 2;
|
184
|
-
}
|
185
|
-
|
186
|
-
header.exception:hover h2,
|
187
|
-
header.exception:hover p {
|
188
|
-
padding-right: 20px;
|
189
|
-
overflow-y: auto;
|
190
|
-
word-wrap: break-word;
|
191
|
-
white-space: pre-wrap;
|
192
|
-
height: auto;
|
193
|
-
max-height: 7.5em;
|
194
|
-
}
|
195
|
-
|
196
|
-
@media screen and (max-width: 1100px) {
|
197
|
-
header.exception {
|
198
|
-
height: auto;
|
199
|
-
}
|
200
|
-
|
201
|
-
header.exception h2,
|
202
|
-
header.exception p {
|
203
|
-
padding-right: 20px;
|
204
|
-
overflow-y: auto;
|
205
|
-
word-wrap: break-word;
|
206
|
-
height: auto;
|
207
|
-
max-height: 7em;
|
208
|
-
}
|
209
|
-
}
|
210
|
-
|
211
|
-
<%#
|
212
|
-
/* Light theme */
|
213
|
-
header.exception {
|
214
|
-
text-shadow: 0 1px 0 rgba(250, 250, 250, 0.6);
|
215
|
-
background: rgba(200,100,50,0.10);
|
216
|
-
color: #977;
|
217
|
-
}
|
218
|
-
header.exception h2 strong {
|
219
|
-
color: #533;
|
220
|
-
}
|
221
|
-
header.exception p {
|
222
|
-
color: #744;
|
223
|
-
}
|
224
|
-
%>
|
225
|
-
|
226
|
-
/* ---------------------------------------------------------------------
|
227
|
-
* Navigation
|
228
|
-
* --------------------------------------------------------------------- */
|
229
|
-
|
230
|
-
nav.tabs {
|
231
|
-
border-bottom: solid 1px #ddd;
|
232
|
-
|
233
|
-
background-color: #eee;
|
234
|
-
text-align: center;
|
235
|
-
|
236
|
-
padding: 6px;
|
237
|
-
|
238
|
-
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
239
|
-
}
|
240
|
-
|
241
|
-
nav.tabs a {
|
242
|
-
display: inline-block;
|
243
|
-
|
244
|
-
height: 22px;
|
245
|
-
line-height: 22px;
|
246
|
-
padding: 0 10px;
|
247
|
-
|
248
|
-
text-decoration: none;
|
249
|
-
font-size: 8pt;
|
250
|
-
font-weight: bold;
|
251
|
-
|
252
|
-
color: #999;
|
253
|
-
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
|
254
|
-
}
|
255
|
-
|
256
|
-
nav.tabs a.selected {
|
257
|
-
color: white;
|
258
|
-
background: rgba(0, 0, 0, 0.5);
|
259
|
-
border-radius: 16px;
|
260
|
-
box-shadow: 1px 1px 0 rgba(255, 255, 255, 0.1);
|
261
|
-
text-shadow: 0 0 4px rgba(0, 0, 0, 0.4), 0 1px 0 rgba(0, 0, 0, 0.4);
|
262
|
-
}
|
263
|
-
|
264
|
-
nav.tabs a.disabled {
|
265
|
-
text-decoration: line-through;
|
266
|
-
text-shadow: none;
|
267
|
-
cursor: default;
|
268
|
-
}
|
269
|
-
|
270
|
-
/* ---------------------------------------------------------------------
|
271
|
-
* Sidebar
|
272
|
-
* --------------------------------------------------------------------- */
|
273
|
-
|
274
|
-
ul.frames {
|
275
|
-
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
276
|
-
}
|
277
|
-
|
278
|
-
/* Each item */
|
279
|
-
ul.frames li {
|
280
|
-
background-color: #f8f8f8;
|
281
|
-
background: -webkit-linear-gradient(top, #f8f8f8 80%, #f0f0f0);
|
282
|
-
background: -moz-linear-gradient(top, #f8f8f8 80%, #f0f0f0);
|
283
|
-
background: linear-gradient(top, #f8f8f8 80%, #f0f0f0);
|
284
|
-
box-shadow: inset 0 -1px 0 #e2e2e2;
|
285
|
-
padding: 7px 20px;
|
286
|
-
|
287
|
-
cursor: pointer;
|
288
|
-
overflow: hidden;
|
289
|
-
}
|
290
|
-
|
291
|
-
ul.frames .name,
|
292
|
-
ul.frames .location {
|
293
|
-
overflow: hidden;
|
294
|
-
height: 1.5em;
|
295
|
-
|
296
|
-
white-space: nowrap;
|
297
|
-
word-wrap: none;
|
298
|
-
text-overflow: ellipsis;
|
299
|
-
}
|
300
|
-
|
301
|
-
ul.frames .method {
|
302
|
-
color: #966;
|
303
|
-
}
|
304
|
-
|
305
|
-
ul.frames .location {
|
306
|
-
font-size: 0.85em;
|
307
|
-
font-weight: 400;
|
308
|
-
color: #999;
|
309
|
-
}
|
310
|
-
|
311
|
-
ul.frames .line {
|
312
|
-
font-weight: bold;
|
313
|
-
}
|
314
|
-
|
315
|
-
/* Selected frame */
|
316
|
-
ul.frames li.selected {
|
317
|
-
background: #38a;
|
318
|
-
box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.1), inset 0 2px 0 rgba(255, 255, 255, 0.01), inset 0 -1px 0 rgba(0, 0, 0, 0.1);
|
319
|
-
}
|
320
|
-
|
321
|
-
ul.frames li.selected .name,
|
322
|
-
ul.frames li.selected .method,
|
323
|
-
ul.frames li.selected .location {
|
324
|
-
color: white;
|
325
|
-
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
|
326
|
-
}
|
327
|
-
|
328
|
-
ul.frames li.selected .location {
|
329
|
-
opacity: 0.6;
|
330
|
-
}
|
331
|
-
|
332
|
-
/* Iconography */
|
333
|
-
ul.frames li {
|
334
|
-
padding-left: 60px;
|
335
|
-
position: relative;
|
336
|
-
}
|
337
|
-
|
338
|
-
ul.frames li .icon {
|
339
|
-
display: block;
|
340
|
-
width: 20px;
|
341
|
-
height: 20px;
|
342
|
-
line-height: 20px;
|
343
|
-
border-radius: 15px;
|
344
|
-
|
345
|
-
text-align: center;
|
346
|
-
|
347
|
-
background: white;
|
348
|
-
border: solid 2px #ccc;
|
349
|
-
|
350
|
-
font-size: 9pt;
|
351
|
-
font-weight: 200;
|
352
|
-
font-style: normal;
|
353
|
-
|
354
|
-
position: absolute;
|
355
|
-
top: 14px;
|
356
|
-
left: 20px;
|
357
|
-
}
|
358
|
-
|
359
|
-
ul.frames .icon.application {
|
360
|
-
background: #808090;
|
361
|
-
border-color: #555;
|
362
|
-
}
|
363
|
-
|
364
|
-
ul.frames .icon.application:before {
|
365
|
-
content: 'A';
|
366
|
-
color: white;
|
367
|
-
text-shadow: 0 0 3px rgba(0, 0, 0, 0.2);
|
368
|
-
}
|
369
|
-
|
370
|
-
/* Responsiveness -- flow to single-line mode */
|
371
|
-
@media screen and (max-width: 1100px) {
|
372
|
-
ul.frames li {
|
373
|
-
padding-top: 6px;
|
374
|
-
padding-bottom: 6px;
|
375
|
-
padding-left: 36px;
|
376
|
-
line-height: 1.3;
|
377
|
-
}
|
378
|
-
|
379
|
-
ul.frames li .icon {
|
380
|
-
width: 11px;
|
381
|
-
height: 11px;
|
382
|
-
line-height: 11px;
|
383
|
-
|
384
|
-
top: 7px;
|
385
|
-
left: 10px;
|
386
|
-
font-size: 5pt;
|
387
|
-
}
|
388
|
-
|
389
|
-
ul.frames .name,
|
390
|
-
ul.frames .location {
|
391
|
-
display: inline-block;
|
392
|
-
line-height: 1.3;
|
393
|
-
height: 1.3em;
|
394
|
-
}
|
395
|
-
|
396
|
-
ul.frames .name {
|
397
|
-
margin-right: 10px;
|
398
|
-
}
|
399
|
-
}
|
400
|
-
|
401
|
-
/* ---------------------------------------------------------------------
|
402
|
-
* Monospace
|
403
|
-
* --------------------------------------------------------------------- */
|
404
|
-
|
405
|
-
pre, code, .be-repl input, .be-repl .command-line span, textarea, .code_linenums {
|
406
|
-
font-family: menlo, lucida console, monospace;
|
407
|
-
font-size: 8pt;
|
408
|
-
}
|
409
|
-
|
410
|
-
/* ---------------------------------------------------------------------
|
411
|
-
* Display area
|
412
|
-
* --------------------------------------------------------------------- */
|
413
|
-
|
414
|
-
.trace_info {
|
415
|
-
background: #fff;
|
416
|
-
padding: 6px;
|
417
|
-
border-radius: 3px;
|
418
|
-
margin-bottom: 2px;
|
419
|
-
box-shadow: 0 0 10px rgba(0, 0, 0, 0.03), 1px 1px 0 rgba(0, 0, 0, 0.05), -1px 1px 0 rgba(0, 0, 0, 0.05), 0 0 0 4px rgba(0, 0, 0, 0.04);
|
420
|
-
}
|
421
|
-
|
422
|
-
.code_block{
|
423
|
-
background: #f1f1f1;
|
424
|
-
border-left: 1px solid #ccc;
|
425
|
-
}
|
426
|
-
|
427
|
-
/* Titlebar */
|
428
|
-
.trace_info .title {
|
429
|
-
background: #f1f1f1;
|
430
|
-
|
431
|
-
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
432
|
-
overflow: hidden;
|
433
|
-
padding: 6px 10px;
|
434
|
-
|
435
|
-
border: solid 1px #ccc;
|
436
|
-
border-bottom: 0;
|
437
|
-
|
438
|
-
border-top-left-radius: 2px;
|
439
|
-
border-top-right-radius: 2px;
|
440
|
-
}
|
441
|
-
|
442
|
-
.trace_info .title .name,
|
443
|
-
.trace_info .title .location {
|
444
|
-
font-size: 9pt;
|
445
|
-
line-height: 26px;
|
446
|
-
height: 26px;
|
447
|
-
overflow: hidden;
|
448
|
-
}
|
449
|
-
|
450
|
-
.trace_info .title .location {
|
451
|
-
float: left;
|
452
|
-
font-weight: bold;
|
453
|
-
font-size: 10pt;
|
454
|
-
}
|
455
|
-
|
456
|
-
.trace_info .title .location a {
|
457
|
-
color:inherit;
|
458
|
-
text-decoration:none;
|
459
|
-
border-bottom:1px solid #aaaaaa;
|
460
|
-
}
|
461
|
-
|
462
|
-
.trace_info .title .location a:hover {
|
463
|
-
border-color:#666666;
|
464
|
-
}
|
465
|
-
|
466
|
-
.trace_info .title .name {
|
467
|
-
float: right;
|
468
|
-
font-weight: 200;
|
469
|
-
}
|
470
|
-
|
471
|
-
.code, .be-console, .unavailable {
|
472
|
-
background: #fff;
|
473
|
-
padding: 5px;
|
474
|
-
|
475
|
-
box-shadow: inset 3px 3px 3px rgba(0, 0, 0, 0.1), inset 0 0 0 1px rgba(0, 0, 0, 0.1);
|
476
|
-
}
|
477
|
-
|
478
|
-
.code_linenums{
|
479
|
-
background:#f1f1f1;
|
480
|
-
padding-top:10px;
|
481
|
-
padding-bottom:9px;
|
482
|
-
float:left;
|
483
|
-
}
|
484
|
-
|
485
|
-
.code_linenums span{
|
486
|
-
display:block;
|
487
|
-
padding:0 12px;
|
488
|
-
}
|
489
|
-
|
490
|
-
.code {
|
491
|
-
margin-bottom: -1px;
|
492
|
-
border-top-left-radius:2px;
|
493
|
-
padding: 10px 0;
|
494
|
-
overflow: auto;
|
495
|
-
}
|
496
|
-
|
497
|
-
.code pre{
|
498
|
-
padding-left:12px;
|
499
|
-
min-height:16px;
|
500
|
-
}
|
501
|
-
|
502
|
-
/* Source unavailable */
|
503
|
-
p.unavailable {
|
504
|
-
padding: 20px 0 40px 0;
|
505
|
-
text-align: center;
|
506
|
-
color: #b99;
|
507
|
-
font-weight: bold;
|
508
|
-
}
|
509
|
-
|
510
|
-
p.unavailable:before {
|
511
|
-
content: '\00d7';
|
512
|
-
display: block;
|
513
|
-
|
514
|
-
color: #daa;
|
515
|
-
|
516
|
-
text-align: center;
|
517
|
-
font-size: 40pt;
|
518
|
-
font-weight: normal;
|
519
|
-
margin-bottom: -10px;
|
520
|
-
}
|
521
|
-
|
522
|
-
@-webkit-keyframes highlight {
|
523
|
-
0% { background: rgba(220, 30, 30, 0.3); }
|
524
|
-
100% { background: rgba(220, 30, 30, 0.1); }
|
525
|
-
}
|
526
|
-
@-moz-keyframes highlight {
|
527
|
-
0% { background: rgba(220, 30, 30, 0.3); }
|
528
|
-
100% { background: rgba(220, 30, 30, 0.1); }
|
529
|
-
}
|
530
|
-
@keyframes highlight {
|
531
|
-
0% { background: rgba(220, 30, 30, 0.3); }
|
532
|
-
100% { background: rgba(220, 30, 30, 0.1); }
|
533
|
-
}
|
534
|
-
|
535
|
-
.code .highlight, .code_linenums .highlight {
|
536
|
-
background: rgba(220, 30, 30, 0.1);
|
537
|
-
-webkit-animation: highlight 400ms linear 1;
|
538
|
-
-moz-animation: highlight 400ms linear 1;
|
539
|
-
animation: highlight 400ms linear 1;
|
540
|
-
}
|
541
|
-
|
542
|
-
/* REPL shell */
|
543
|
-
.be-console {
|
544
|
-
padding: 0 1px 10px 1px;
|
545
|
-
border-bottom-left-radius: 2px;
|
546
|
-
border-bottom-right-radius: 2px;
|
547
|
-
}
|
548
|
-
|
549
|
-
.be-console pre {
|
550
|
-
padding: 10px 10px 0 10px;
|
551
|
-
max-height: 400px;
|
552
|
-
overflow-x: none;
|
553
|
-
overflow-y: auto;
|
554
|
-
margin-bottom: -3px;
|
555
|
-
word-wrap: break-word;
|
556
|
-
white-space: pre-wrap;
|
557
|
-
}
|
558
|
-
|
559
|
-
/* .command-line > span + input */
|
560
|
-
.be-console .command-line {
|
561
|
-
display: table;
|
562
|
-
width: 100%;
|
563
|
-
}
|
564
|
-
|
565
|
-
.be-console .command-line span,
|
566
|
-
.be-console .command-line input {
|
567
|
-
display: table-cell;
|
568
|
-
}
|
569
|
-
|
570
|
-
.be-console .command-line span {
|
571
|
-
width: 1%;
|
572
|
-
padding-right: 5px;
|
573
|
-
padding-left: 10px;
|
574
|
-
white-space: pre;
|
575
|
-
}
|
576
|
-
|
577
|
-
.be-console .command-line input {
|
578
|
-
width: 99%;
|
579
|
-
}
|
580
|
-
|
581
|
-
/* Input box */
|
582
|
-
.be-console input,
|
583
|
-
.be-console input:focus {
|
584
|
-
outline: 0;
|
585
|
-
border: 0;
|
586
|
-
padding: 0;
|
587
|
-
background: transparent;
|
588
|
-
margin: 0;
|
589
|
-
}
|
590
|
-
|
591
|
-
/* Hint text */
|
592
|
-
.hint {
|
593
|
-
margin: 15px 0 20px 0;
|
594
|
-
font-size: 8pt;
|
595
|
-
color: #8080a0;
|
596
|
-
padding-left: 20px;
|
597
|
-
}
|
598
|
-
|
599
|
-
.hint:before {
|
600
|
-
content: '\25b2';
|
601
|
-
margin-right: 5px;
|
602
|
-
opacity: 0.5;
|
603
|
-
}
|
604
|
-
|
605
|
-
/* ---------------------------------------------------------------------
|
606
|
-
* Variable infos
|
607
|
-
* --------------------------------------------------------------------- */
|
608
|
-
|
609
|
-
.sub {
|
610
|
-
padding: 10px 0;
|
611
|
-
margin: 10px 0;
|
612
|
-
}
|
613
|
-
|
614
|
-
.sub:before {
|
615
|
-
content: '';
|
616
|
-
display: block;
|
617
|
-
width: 100%;
|
618
|
-
height: 4px;
|
619
|
-
|
620
|
-
border-radius: 2px;
|
621
|
-
background: rgba(0, 150, 200, 0.05);
|
622
|
-
box-shadow: 1px 1px 0 rgba(255, 255, 255, 0.7), inset 0 0 0 1px rgba(0, 0, 0, 0.04), inset 2px 2px 2px rgba(0, 0, 0, 0.07);
|
623
|
-
}
|
624
|
-
|
625
|
-
.sub h3 {
|
626
|
-
color: #39a;
|
627
|
-
font-size: 1.1em;
|
628
|
-
margin: 10px 0;
|
629
|
-
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
|
630
|
-
|
631
|
-
-webkit-font-smoothing: antialiased;
|
632
|
-
}
|
633
|
-
|
634
|
-
.sub .inset {
|
635
|
-
overflow-y: auto;
|
636
|
-
}
|
637
|
-
|
638
|
-
.sub table {
|
639
|
-
table-layout: fixed;
|
640
|
-
}
|
641
|
-
|
642
|
-
.sub table td {
|
643
|
-
border-top: dotted 1px #ddd;
|
644
|
-
padding: 7px 1px;
|
645
|
-
}
|
646
|
-
|
647
|
-
.sub table td.name {
|
648
|
-
width: 150px;
|
649
|
-
|
650
|
-
font-weight: bold;
|
651
|
-
font-size: 0.8em;
|
652
|
-
padding-right: 20px;
|
653
|
-
|
654
|
-
word-wrap: break-word;
|
655
|
-
}
|
656
|
-
|
657
|
-
.sub table td pre {
|
658
|
-
max-height: 15em;
|
659
|
-
overflow-y: auto;
|
660
|
-
}
|
661
|
-
|
662
|
-
.sub table td pre {
|
663
|
-
width: 100%;
|
664
|
-
|
665
|
-
word-wrap: break-word;
|
666
|
-
white-space: normal;
|
667
|
-
}
|
668
|
-
|
669
|
-
/* "(object doesn't support inspect)" */
|
670
|
-
.sub .unsupported {
|
671
|
-
font-family: sans-serif;
|
672
|
-
color: #777;
|
673
|
-
}
|
674
|
-
|
675
|
-
/* ---------------------------------------------------------------------
|
676
|
-
* Scrollbar
|
677
|
-
* --------------------------------------------------------------------- */
|
678
|
-
|
679
|
-
nav.sidebar::-webkit-scrollbar,
|
680
|
-
.inset pre::-webkit-scrollbar,
|
681
|
-
.be-console pre::-webkit-scrollbar,
|
682
|
-
.code::-webkit-scrollbar {
|
683
|
-
width: 10px;
|
684
|
-
height: 10px;
|
685
|
-
}
|
686
|
-
|
687
|
-
.inset pre::-webkit-scrollbar-thumb,
|
688
|
-
.be-console pre::-webkit-scrollbar-thumb,
|
689
|
-
.code::-webkit-scrollbar-thumb {
|
690
|
-
background: #ccc;
|
691
|
-
border-radius: 5px;
|
692
|
-
}
|
693
|
-
|
694
|
-
nav.sidebar::-webkit-scrollbar-thumb {
|
695
|
-
background: rgba(0, 0, 0, 0.0);
|
696
|
-
border-radius: 5px;
|
697
|
-
}
|
698
|
-
|
699
|
-
nav.sidebar:hover::-webkit-scrollbar-thumb {
|
700
|
-
background-color: #999;
|
701
|
-
background: -webkit-linear-gradient(left, #aaa, #999);
|
702
|
-
}
|
703
|
-
|
704
|
-
.be-console pre:hover::-webkit-scrollbar-thumb,
|
705
|
-
.inset pre:hover::-webkit-scrollbar-thumb,
|
706
|
-
.code:hover::-webkit-scrollbar-thumb {
|
707
|
-
background: #888;
|
708
|
-
}
|
709
|
-
</style>
|
9
|
+
<%== ErrorPageStyle.style_tag(csp_nonce) %>
|
710
10
|
|
711
11
|
<%# IE8 compatibility crap %>
|
712
|
-
<script>
|
12
|
+
<script nonce="<%= csp_nonce %>">
|
713
13
|
(function() {
|
714
14
|
var elements = ["section", "nav", "header", "footer", "audio"];
|
715
15
|
for (var i = 0; i < elements.length; i++) {
|
@@ -723,7 +23,7 @@
|
|
723
23
|
rendered in the host app's layout. Let's empty out the styles of the
|
724
24
|
host app.
|
725
25
|
%>
|
726
|
-
<script>
|
26
|
+
<script nonce="<%= csp_nonce %>">
|
727
27
|
if (window.Turbolinks) {
|
728
28
|
for(var i=0; i < document.styleSheets.length; i++) {
|
729
29
|
if(document.styleSheets[i].href)
|
@@ -748,6 +48,15 @@
|
|
748
48
|
}
|
749
49
|
</script>
|
750
50
|
|
51
|
+
<p class='no-inline-style-notice'>
|
52
|
+
<strong>
|
53
|
+
Better Errors can't apply inline style<span class='no-javascript-notice'> (or run Javascript)</span>,
|
54
|
+
possibly because you have a Content Security Policy along with Turbolinks.
|
55
|
+
But you can
|
56
|
+
<a href='/__better_errors' target="_blank">open the interactive console in a new tab/window</a>.
|
57
|
+
</strong>
|
58
|
+
</p>
|
59
|
+
|
751
60
|
<div class='top'>
|
752
61
|
<header class="exception">
|
753
62
|
<h2><strong><%= exception_type %></strong> <span>at <%= request_path %></span></h2>
|
@@ -764,6 +73,9 @@
|
|
764
73
|
<% end %>
|
765
74
|
</div>
|
766
75
|
<% end %>
|
76
|
+
<% if exception_hint %>
|
77
|
+
<h2>Hint: <%= exception_hint %></h2>
|
78
|
+
<% end %>
|
767
79
|
</header>
|
768
80
|
</div>
|
769
81
|
|
@@ -791,21 +103,37 @@
|
|
791
103
|
</ul>
|
792
104
|
</nav>
|
793
105
|
|
794
|
-
|
795
|
-
<div class="frame_info
|
796
|
-
|
106
|
+
<div class="frameInfos">
|
107
|
+
<div class="frame_info current" data-frame-idx="0">
|
108
|
+
<p class='no-javascript-notice'>
|
109
|
+
Better Errors can't run Javascript here<span class='no-inline-style-notice'> (or apply inline style)</span>,
|
110
|
+
possibly because you have a Content Security Policy along with Turbolinks.
|
111
|
+
But you can
|
112
|
+
<a href='/__better_errors' target="_blank">open the interactive console in a new tab/window</a>.
|
113
|
+
</p>
|
114
|
+
<!-- this is enough information to show something in case JS doesn't get to load -->
|
115
|
+
<%== ErrorPage.render_template('variable_info', first_frame_variable_info) %>
|
116
|
+
</div>
|
117
|
+
</div>
|
797
118
|
</section>
|
798
119
|
</body>
|
799
|
-
<script>
|
120
|
+
<script nonce="<%= csp_nonce %>">
|
800
121
|
(function() {
|
801
122
|
|
802
123
|
var OID = "<%= id %>";
|
803
124
|
var csrfToken = "<%= csrf_token %>";
|
804
125
|
|
805
126
|
var previousFrame = null;
|
806
|
-
var previousFrameInfo = null;
|
807
127
|
var allFrames = document.querySelectorAll("ul.frames li");
|
808
|
-
var
|
128
|
+
var frameInfos = document.querySelector(".frameInfos");
|
129
|
+
|
130
|
+
document.querySelector('body').classList.remove("better-errors-javascript-not-loaded");
|
131
|
+
document.querySelector('body').classList.add("better-errors-javascript-loaded");
|
132
|
+
|
133
|
+
var noJSNotices = document.querySelectorAll('.no-javascript-notice');
|
134
|
+
for(var i = 0; i < noJSNotices.length; i++) {
|
135
|
+
noJSNotices[i].remove();
|
136
|
+
}
|
809
137
|
|
810
138
|
function apiCall(method, opts, cb) {
|
811
139
|
var req = new XMLHttpRequest();
|
@@ -825,6 +153,28 @@
|
|
825
153
|
return html.replace(/&/, "&").replace(/</g, "<");
|
826
154
|
}
|
827
155
|
|
156
|
+
function hasConsoleBeenUsedPreviously() {
|
157
|
+
return !!document.cookie.split('; ').find(function(cookie) {
|
158
|
+
return cookie.startsWith('BetterErrors-has-used-console=');
|
159
|
+
});
|
160
|
+
}
|
161
|
+
|
162
|
+
var consoleHasBeenUsed = hasConsoleBeenUsedPreviously();
|
163
|
+
|
164
|
+
function consoleWasJustUsed() {
|
165
|
+
if (consoleHasBeenUsed) {
|
166
|
+
return;
|
167
|
+
}
|
168
|
+
|
169
|
+
hideConsoleHint();
|
170
|
+
consoleHasBeenUsed = true;
|
171
|
+
document.cookie = "BetterErrors-has-used-console=true;path=/;max-age=31536000;samesite"
|
172
|
+
}
|
173
|
+
|
174
|
+
function hideConsoleHint() {
|
175
|
+
document.querySelector('body').className += " console-has-been-used";
|
176
|
+
}
|
177
|
+
|
828
178
|
function REPL(index) {
|
829
179
|
this.index = index;
|
830
180
|
|
@@ -846,15 +196,20 @@
|
|
846
196
|
this.inputElement = this.container.querySelector("input");
|
847
197
|
this.outputElement = this.container.querySelector("pre");
|
848
198
|
|
199
|
+
if (consoleHasBeenUsed) {
|
200
|
+
hideConsoleHint();
|
201
|
+
}
|
202
|
+
|
849
203
|
var self = this;
|
850
204
|
this.inputElement.onkeydown = function(ev) {
|
851
205
|
self.onKeyDown(ev);
|
206
|
+
consoleWasJustUsed();
|
852
207
|
};
|
853
208
|
|
854
209
|
this.setPrompt(">>");
|
855
210
|
|
856
211
|
REPL.all[this.index] = this;
|
857
|
-
}
|
212
|
+
};
|
858
213
|
|
859
214
|
REPL.prototype.focus = function() {
|
860
215
|
this.inputElement.focus();
|
@@ -894,7 +249,7 @@
|
|
894
249
|
self.writeOutput(response.error + "\n");
|
895
250
|
}
|
896
251
|
self.writeOutput(self._prompt + " ");
|
897
|
-
self.writeRawOutput(response.highlighted_input + "
|
252
|
+
self.writeRawOutput("<span class='syntax-highlighted'>" + response.highlighted_input + "</span>\n");
|
898
253
|
self.writeOutput(response.result);
|
899
254
|
self.setPrompt(response.prompt);
|
900
255
|
self.setInput(response.prefilled_input);
|
@@ -952,17 +307,25 @@
|
|
952
307
|
};
|
953
308
|
|
954
309
|
function switchTo(el) {
|
955
|
-
|
956
|
-
|
310
|
+
var currentFrameInfo = document.querySelectorAll('.frame_info.current');
|
311
|
+
for(var i = 0; i < currentFrameInfo.length; i++) {
|
312
|
+
currentFrameInfo[i].className = "frame_info";
|
313
|
+
}
|
957
314
|
|
958
|
-
el.
|
315
|
+
el.className = "frame_info current";
|
959
316
|
|
960
317
|
var replInput = el.querySelector('.be-console input');
|
961
318
|
if (replInput) replInput.focus();
|
962
319
|
}
|
963
320
|
|
964
321
|
function selectFrameInfo(index) {
|
965
|
-
var el =
|
322
|
+
var el = document.querySelector(".frame_info[data-frame-idx='" + index + "']")
|
323
|
+
if (!el) {
|
324
|
+
el = document.createElement("div");
|
325
|
+
el.className = "frame_info";
|
326
|
+
el.setAttribute('data-frame-idx', index);
|
327
|
+
frameInfos.appendChild(el);
|
328
|
+
}
|
966
329
|
if(el) {
|
967
330
|
if (el.loaded) {
|
968
331
|
return switchTo(el);
|