better_errors-creditkudos 2.1.1

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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.travis.yml +8 -0
  4. data/.yardopts +1 -0
  5. data/CHANGELOG.md +3 -0
  6. data/Gemfile +11 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +128 -0
  9. data/Rakefile +13 -0
  10. data/better_errors-creditkudos.gemspec +28 -0
  11. data/lib/better_errors.rb +152 -0
  12. data/lib/better_errors/code_formatter.rb +63 -0
  13. data/lib/better_errors/code_formatter/html.rb +26 -0
  14. data/lib/better_errors/code_formatter/text.rb +14 -0
  15. data/lib/better_errors/error_page.rb +129 -0
  16. data/lib/better_errors/exception_extension.rb +17 -0
  17. data/lib/better_errors/middleware.rb +141 -0
  18. data/lib/better_errors/rails.rb +28 -0
  19. data/lib/better_errors/raised_exception.rb +68 -0
  20. data/lib/better_errors/repl.rb +30 -0
  21. data/lib/better_errors/repl/basic.rb +20 -0
  22. data/lib/better_errors/repl/pry.rb +78 -0
  23. data/lib/better_errors/stack_frame.rb +111 -0
  24. data/lib/better_errors/templates/main.erb +1032 -0
  25. data/lib/better_errors/templates/text.erb +21 -0
  26. data/lib/better_errors/templates/variable_info.erb +72 -0
  27. data/lib/better_errors/version.rb +3 -0
  28. data/spec/better_errors/code_formatter_spec.rb +92 -0
  29. data/spec/better_errors/error_page_spec.rb +122 -0
  30. data/spec/better_errors/middleware_spec.rb +180 -0
  31. data/spec/better_errors/raised_exception_spec.rb +72 -0
  32. data/spec/better_errors/repl/basic_spec.rb +18 -0
  33. data/spec/better_errors/repl/pry_spec.rb +40 -0
  34. data/spec/better_errors/repl/shared_examples.rb +18 -0
  35. data/spec/better_errors/stack_frame_spec.rb +157 -0
  36. data/spec/better_errors/support/my_source.rb +20 -0
  37. data/spec/better_errors_spec.rb +73 -0
  38. data/spec/spec_helper.rb +5 -0
  39. data/spec/without_binding_of_caller.rb +9 -0
  40. metadata +136 -0
@@ -0,0 +1,20 @@
1
+ module BetterErrors
2
+ module REPL
3
+ class Basic
4
+ def initialize(binding)
5
+ @binding = binding
6
+ end
7
+
8
+ def send_input(str)
9
+ [execute(str), ">>", ""]
10
+ end
11
+
12
+ private
13
+ def execute(str)
14
+ "=> #{@binding.eval(str).inspect}\n"
15
+ rescue Exception => e
16
+ "!! #{e.inspect rescue e.class.to_s rescue "Exception"}\n"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,78 @@
1
+ require "fiber"
2
+ require "pry"
3
+
4
+ module BetterErrors
5
+ module REPL
6
+ class Pry
7
+ class Input
8
+ def readline
9
+ Fiber.yield
10
+ end
11
+ end
12
+
13
+ class Output
14
+ def initialize
15
+ @buffer = ""
16
+ end
17
+
18
+ def puts(*args)
19
+ args.each do |arg|
20
+ @buffer << "#{arg.chomp}\n"
21
+ end
22
+ end
23
+
24
+ def tty?
25
+ false
26
+ end
27
+
28
+ def read_buffer
29
+ @buffer
30
+ ensure
31
+ @buffer = ""
32
+ end
33
+ end
34
+
35
+ def initialize(binding)
36
+ @fiber = Fiber.new do
37
+ @pry.repl binding
38
+ end
39
+ @input = BetterErrors::REPL::Pry::Input.new
40
+ @output = BetterErrors::REPL::Pry::Output.new
41
+ @pry = ::Pry.new input: @input, output: @output
42
+ @pry.hooks.clear_all if defined?(@pry.hooks.clear_all)
43
+ @fiber.resume
44
+ end
45
+
46
+ def send_input(str)
47
+ local ::Pry.config, color: false, pager: false do
48
+ @fiber.resume "#{str}\n"
49
+ [@output.read_buffer, *prompt]
50
+ end
51
+ end
52
+
53
+ def prompt
54
+ if indent = @pry.instance_variable_get(:@indent) and !indent.indent_level.empty?
55
+ ["..", indent.indent_level]
56
+ else
57
+ [">>", ""]
58
+ end
59
+ rescue
60
+ [">>", ""]
61
+ end
62
+
63
+ private
64
+ def local(obj, attrs)
65
+ old_attrs = {}
66
+ attrs.each do |k, v|
67
+ old_attrs[k] = obj.send k
68
+ obj.send "#{k}=", v
69
+ end
70
+ yield
71
+ ensure
72
+ old_attrs.each do |k, v|
73
+ obj.send "#{k}=", v
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,111 @@
1
+ require "set"
2
+
3
+ module BetterErrors
4
+ # @private
5
+ class StackFrame
6
+ def self.from_exception(exception)
7
+ RaisedException.new(exception).backtrace
8
+ end
9
+
10
+ attr_reader :filename, :line, :name, :frame_binding
11
+
12
+ def initialize(filename, line, name, frame_binding = nil)
13
+ @filename = filename
14
+ @line = line
15
+ @name = name
16
+ @frame_binding = frame_binding
17
+
18
+ set_pretty_method_name if frame_binding
19
+ end
20
+
21
+ def application?
22
+ if root = BetterErrors.application_root
23
+ filename.index(root) == 0 && filename.index("#{root}/vendor") != 0
24
+ end
25
+ end
26
+
27
+ def application_path
28
+ filename[(BetterErrors.application_root.length+1)..-1]
29
+ end
30
+
31
+ def gem?
32
+ Gem.path.any? { |path| filename.index(path) == 0 }
33
+ end
34
+
35
+ def gem_path
36
+ if path = Gem.path.detect { |p| filename.index(p) == 0 }
37
+ gem_name_and_version, path = filename.sub("#{path}/gems/", "").split("/", 2)
38
+ /(?<gem_name>.+)-(?<gem_version>[\w.]+)/ =~ gem_name_and_version
39
+ "#{gem_name} (#{gem_version}) #{path}"
40
+ end
41
+ end
42
+
43
+ def class_name
44
+ @class_name
45
+ end
46
+
47
+ def method_name
48
+ @method_name || @name
49
+ end
50
+
51
+ def context
52
+ if gem?
53
+ :gem
54
+ elsif application?
55
+ :application
56
+ else
57
+ :dunno
58
+ end
59
+ end
60
+
61
+ def pretty_path
62
+ case context
63
+ when :application; application_path
64
+ when :gem; gem_path
65
+ else filename
66
+ end
67
+ end
68
+
69
+ def local_variables
70
+ return {} unless frame_binding
71
+ frame_binding.eval("local_variables").each_with_object({}) do |name, hash|
72
+ if defined?(frame_binding.local_variable_get)
73
+ hash[name] = frame_binding.local_variable_get(name)
74
+ else
75
+ hash[name] = frame_binding.eval(name.to_s)
76
+ end
77
+ end
78
+ end
79
+
80
+ def instance_variables
81
+ return {} unless frame_binding
82
+ Hash[visible_instance_variables.map { |x|
83
+ [x, frame_binding.eval(x.to_s)]
84
+ }]
85
+ end
86
+
87
+ def visible_instance_variables
88
+ frame_binding.eval("instance_variables") - BetterErrors.ignored_instance_variables
89
+ end
90
+
91
+ def to_s
92
+ "#{pretty_path}:#{line}:in `#{name}'"
93
+ end
94
+
95
+ private
96
+ def set_pretty_method_name
97
+ name =~ /\A(block (\([^)]+\) )?in )?/
98
+ recv = frame_binding.eval("self")
99
+
100
+ return unless method_name = frame_binding.eval("::Kernel.__method__")
101
+
102
+ if Module === recv
103
+ @class_name = "#{$1}#{recv}"
104
+ @method_name = ".#{method_name}"
105
+ else
106
+ @class_name = "#{$1}#{Kernel.instance_method(:class).bind(recv).call}"
107
+ @method_name = "##{method_name}"
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,1032 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title><%= exception_type %> at <%= request_path %></title>
5
+ </head>
6
+ <body>
7
+ <%# Stylesheets are placed in the <body> for Turbolinks compatibility. %>
8
+ <style>
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 h2 {
150
+ font-weight: 200;
151
+ font-size: 11pt;
152
+ }
153
+
154
+ header.exception h2,
155
+ header.exception p {
156
+ line-height: 1.4em;
157
+ overflow: hidden;
158
+ white-space: pre;
159
+ text-overflow: ellipsis;
160
+ }
161
+
162
+ header.exception h2 strong {
163
+ font-weight: 700;
164
+ color: #d55;
165
+ }
166
+
167
+ header.exception p {
168
+ font-weight: 200;
169
+ font-size: 20pt;
170
+ color: white;
171
+ }
172
+
173
+ header.exception:hover {
174
+ height: auto;
175
+ z-index: 2;
176
+ }
177
+
178
+ header.exception:hover h2,
179
+ header.exception:hover p {
180
+ padding-right: 20px;
181
+ overflow-y: auto;
182
+ word-wrap: break-word;
183
+ white-space: pre-wrap;
184
+ height: auto;
185
+ max-height: 7.5em;
186
+ }
187
+
188
+ @media screen and (max-width: 1100px) {
189
+ header.exception {
190
+ height: auto;
191
+ }
192
+
193
+ header.exception h2,
194
+ header.exception p {
195
+ padding-right: 20px;
196
+ overflow-y: auto;
197
+ word-wrap: break-word;
198
+ height: auto;
199
+ max-height: 7em;
200
+ }
201
+ }
202
+
203
+ <%#
204
+ /* Light theme */
205
+ header.exception {
206
+ text-shadow: 0 1px 0 rgba(250, 250, 250, 0.6);
207
+ background: rgba(200,100,50,0.10);
208
+ color: #977;
209
+ }
210
+ header.exception h2 strong {
211
+ color: #533;
212
+ }
213
+ header.exception p {
214
+ color: #744;
215
+ }
216
+ %>
217
+
218
+ /* ---------------------------------------------------------------------
219
+ * Navigation
220
+ * --------------------------------------------------------------------- */
221
+
222
+ nav.tabs {
223
+ border-bottom: solid 1px #ddd;
224
+
225
+ background-color: #eee;
226
+ text-align: center;
227
+
228
+ padding: 6px;
229
+
230
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
231
+ }
232
+
233
+ nav.tabs a {
234
+ display: inline-block;
235
+
236
+ height: 22px;
237
+ line-height: 22px;
238
+ padding: 0 10px;
239
+
240
+ text-decoration: none;
241
+ font-size: 8pt;
242
+ font-weight: bold;
243
+
244
+ color: #999;
245
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
246
+ }
247
+
248
+ nav.tabs a.selected {
249
+ color: white;
250
+ background: rgba(0, 0, 0, 0.5);
251
+ border-radius: 16px;
252
+ box-shadow: 1px 1px 0 rgba(255, 255, 255, 0.1);
253
+ text-shadow: 0 0 4px rgba(0, 0, 0, 0.4), 0 1px 0 rgba(0, 0, 0, 0.4);
254
+ }
255
+
256
+ nav.tabs a.disabled {
257
+ text-decoration: line-through;
258
+ text-shadow: none;
259
+ cursor: default;
260
+ }
261
+
262
+ /* ---------------------------------------------------------------------
263
+ * Sidebar
264
+ * --------------------------------------------------------------------- */
265
+
266
+ ul.frames {
267
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
268
+ }
269
+
270
+ /* Each item */
271
+ ul.frames li {
272
+ background-color: #f8f8f8;
273
+ background: -webkit-linear-gradient(top, #f8f8f8 80%, #f0f0f0);
274
+ background: -moz-linear-gradient(top, #f8f8f8 80%, #f0f0f0);
275
+ background: linear-gradient(top, #f8f8f8 80%, #f0f0f0);
276
+ box-shadow: inset 0 -1px 0 #e2e2e2;
277
+ padding: 7px 20px;
278
+
279
+ cursor: pointer;
280
+ overflow: hidden;
281
+ }
282
+
283
+ ul.frames .name,
284
+ ul.frames .location {
285
+ overflow: hidden;
286
+ height: 1.5em;
287
+
288
+ white-space: nowrap;
289
+ word-wrap: none;
290
+ text-overflow: ellipsis;
291
+ }
292
+
293
+ ul.frames .method {
294
+ color: #966;
295
+ }
296
+
297
+ ul.frames .location {
298
+ font-size: 0.85em;
299
+ font-weight: 400;
300
+ color: #999;
301
+ }
302
+
303
+ ul.frames .line {
304
+ font-weight: bold;
305
+ }
306
+
307
+ /* Selected frame */
308
+ ul.frames li.selected {
309
+ background: #38a;
310
+ 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);
311
+ }
312
+
313
+ ul.frames li.selected .name,
314
+ ul.frames li.selected .method,
315
+ ul.frames li.selected .location {
316
+ color: white;
317
+ text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
318
+ }
319
+
320
+ ul.frames li.selected .location {
321
+ opacity: 0.6;
322
+ }
323
+
324
+ /* Iconography */
325
+ ul.frames li {
326
+ padding-left: 60px;
327
+ position: relative;
328
+ }
329
+
330
+ ul.frames li .icon {
331
+ display: block;
332
+ width: 20px;
333
+ height: 20px;
334
+ line-height: 20px;
335
+ border-radius: 15px;
336
+
337
+ text-align: center;
338
+
339
+ background: white;
340
+ border: solid 2px #ccc;
341
+
342
+ font-size: 9pt;
343
+ font-weight: 200;
344
+ font-style: normal;
345
+
346
+ position: absolute;
347
+ top: 14px;
348
+ left: 20px;
349
+ }
350
+
351
+ ul.frames .icon.application {
352
+ background: #808090;
353
+ border-color: #555;
354
+ }
355
+
356
+ ul.frames .icon.application:before {
357
+ content: 'A';
358
+ color: white;
359
+ text-shadow: 0 0 3px rgba(0, 0, 0, 0.2);
360
+ }
361
+
362
+ /* Responsiveness -- flow to single-line mode */
363
+ @media screen and (max-width: 1100px) {
364
+ ul.frames li {
365
+ padding-top: 6px;
366
+ padding-bottom: 6px;
367
+ padding-left: 36px;
368
+ line-height: 1.3;
369
+ }
370
+
371
+ ul.frames li .icon {
372
+ width: 11px;
373
+ height: 11px;
374
+ line-height: 11px;
375
+
376
+ top: 7px;
377
+ left: 10px;
378
+ font-size: 5pt;
379
+ }
380
+
381
+ ul.frames .name,
382
+ ul.frames .location {
383
+ display: inline-block;
384
+ line-height: 1.3;
385
+ height: 1.3em;
386
+ }
387
+
388
+ ul.frames .name {
389
+ margin-right: 10px;
390
+ }
391
+ }
392
+
393
+ /* ---------------------------------------------------------------------
394
+ * Monospace
395
+ * --------------------------------------------------------------------- */
396
+
397
+ pre, code, .repl input, .repl .prompt span, textarea, .code_linenums {
398
+ font-family: menlo, lucida console, monospace;
399
+ font-size: 8pt;
400
+ }
401
+
402
+ /* ---------------------------------------------------------------------
403
+ * Display area
404
+ * --------------------------------------------------------------------- */
405
+
406
+ .trace_info {
407
+ background: #fff;
408
+ padding: 6px;
409
+ border-radius: 3px;
410
+ margin-bottom: 2px;
411
+ 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);
412
+ }
413
+
414
+ .code_block{
415
+ background: #f1f1f1;
416
+ border-left: 1px solid #ccc;
417
+ }
418
+
419
+ /* Titlebar */
420
+ .trace_info .title {
421
+ background: #f1f1f1;
422
+
423
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3);
424
+ overflow: hidden;
425
+ padding: 6px 10px;
426
+
427
+ border: solid 1px #ccc;
428
+ border-bottom: 0;
429
+
430
+ border-top-left-radius: 2px;
431
+ border-top-right-radius: 2px;
432
+ }
433
+
434
+ .trace_info .title .name,
435
+ .trace_info .title .location {
436
+ font-size: 9pt;
437
+ line-height: 26px;
438
+ height: 26px;
439
+ overflow: hidden;
440
+ }
441
+
442
+ .trace_info .title .location {
443
+ float: left;
444
+ font-weight: bold;
445
+ font-size: 10pt;
446
+ }
447
+
448
+ .trace_info .title .location a {
449
+ color:inherit;
450
+ text-decoration:none;
451
+ border-bottom:1px solid #aaaaaa;
452
+ }
453
+
454
+ .trace_info .title .location a:hover {
455
+ border-color:#666666;
456
+ }
457
+
458
+ .trace_info .title .name {
459
+ float: right;
460
+ font-weight: 200;
461
+ }
462
+
463
+ .code, .console, .unavailable {
464
+ background: #fff;
465
+ padding: 5px;
466
+
467
+ box-shadow: inset 3px 3px 3px rgba(0, 0, 0, 0.1), inset 0 0 0 1px rgba(0, 0, 0, 0.1);
468
+ }
469
+
470
+ .code_linenums{
471
+ background:#f1f1f1;
472
+ padding-top:10px;
473
+ padding-bottom:9px;
474
+ float:left;
475
+ }
476
+
477
+ .code_linenums span{
478
+ display:block;
479
+ padding:0 12px;
480
+ }
481
+
482
+ .code {
483
+ margin-bottom: -1px;
484
+ border-top-left-radius:2px;
485
+ padding: 10px 0;
486
+ overflow: auto;
487
+ }
488
+
489
+ .code pre{
490
+ padding-left:12px;
491
+ min-height:16px;
492
+ }
493
+
494
+ /* Source unavailable */
495
+ p.unavailable {
496
+ padding: 20px 0 40px 0;
497
+ text-align: center;
498
+ color: #b99;
499
+ font-weight: bold;
500
+ }
501
+
502
+ p.unavailable:before {
503
+ content: '\00d7';
504
+ display: block;
505
+
506
+ color: #daa;
507
+
508
+ text-align: center;
509
+ font-size: 40pt;
510
+ font-weight: normal;
511
+ margin-bottom: -10px;
512
+ }
513
+
514
+ @-webkit-keyframes highlight {
515
+ 0% { background: rgba(220, 30, 30, 0.3); }
516
+ 100% { background: rgba(220, 30, 30, 0.1); }
517
+ }
518
+ @-moz-keyframes highlight {
519
+ 0% { background: rgba(220, 30, 30, 0.3); }
520
+ 100% { background: rgba(220, 30, 30, 0.1); }
521
+ }
522
+ @keyframes highlight {
523
+ 0% { background: rgba(220, 30, 30, 0.3); }
524
+ 100% { background: rgba(220, 30, 30, 0.1); }
525
+ }
526
+
527
+ .code .highlight, .code_linenums .highlight {
528
+ background: rgba(220, 30, 30, 0.1);
529
+ -webkit-animation: highlight 400ms linear 1;
530
+ -moz-animation: highlight 400ms linear 1;
531
+ animation: highlight 400ms linear 1;
532
+ }
533
+
534
+ /* REPL shell */
535
+ .console {
536
+ padding: 0 1px 10px 1px;
537
+ border-bottom-left-radius: 2px;
538
+ border-bottom-right-radius: 2px;
539
+ }
540
+
541
+ .console pre {
542
+ padding: 10px 10px 0 10px;
543
+ max-height: 400px;
544
+ overflow-x: none;
545
+ overflow-y: auto;
546
+ margin-bottom: -3px;
547
+ word-wrap: break-word;
548
+ white-space: pre-wrap;
549
+ }
550
+
551
+ /* .prompt > span + input */
552
+ .console .prompt {
553
+ display: table;
554
+ width: 100%;
555
+ }
556
+
557
+ .console .prompt span,
558
+ .console .prompt input {
559
+ display: table-cell;
560
+ }
561
+
562
+ .console .prompt span {
563
+ width: 1%;
564
+ padding-right: 5px;
565
+ padding-left: 10px;
566
+ }
567
+
568
+ .console .prompt input {
569
+ width: 99%;
570
+ }
571
+
572
+ /* Input box */
573
+ .console input,
574
+ .console input:focus {
575
+ outline: 0;
576
+ border: 0;
577
+ padding: 0;
578
+ background: transparent;
579
+ margin: 0;
580
+ }
581
+
582
+ /* Hint text */
583
+ .hint {
584
+ margin: 15px 0 20px 0;
585
+ font-size: 8pt;
586
+ color: #8080a0;
587
+ padding-left: 20px;
588
+ }
589
+
590
+ .hint:before {
591
+ content: '\25b2';
592
+ margin-right: 5px;
593
+ opacity: 0.5;
594
+ }
595
+
596
+ /* ---------------------------------------------------------------------
597
+ * Variable infos
598
+ * --------------------------------------------------------------------- */
599
+
600
+ .sub {
601
+ padding: 10px 0;
602
+ margin: 10px 0;
603
+ }
604
+
605
+ .sub:before {
606
+ content: '';
607
+ display: block;
608
+ width: 100%;
609
+ height: 4px;
610
+
611
+ border-radius: 2px;
612
+ background: rgba(0, 150, 200, 0.05);
613
+ 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);
614
+ }
615
+
616
+ .sub h3 {
617
+ color: #39a;
618
+ font-size: 1.1em;
619
+ margin: 10px 0;
620
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
621
+
622
+ -webkit-font-smoothing: antialiased;
623
+ }
624
+
625
+ .sub .inset {
626
+ overflow-y: auto;
627
+ }
628
+
629
+ .sub table {
630
+ table-layout: fixed;
631
+ }
632
+
633
+ .sub table td {
634
+ border-top: dotted 1px #ddd;
635
+ padding: 7px 1px;
636
+ }
637
+
638
+ .sub table td.name {
639
+ width: 150px;
640
+
641
+ font-weight: bold;
642
+ font-size: 0.8em;
643
+ padding-right: 20px;
644
+
645
+ word-wrap: break-word;
646
+ }
647
+
648
+ .sub table td pre {
649
+ max-height: 15em;
650
+ overflow-y: auto;
651
+ }
652
+
653
+ .sub table td pre {
654
+ width: 100%;
655
+
656
+ word-wrap: break-word;
657
+ white-space: normal;
658
+ }
659
+
660
+ /* "(object doesn't support inspect)" */
661
+ .sub .unsupported {
662
+ font-family: sans-serif;
663
+ color: #777;
664
+ }
665
+
666
+ /* ---------------------------------------------------------------------
667
+ * Scrollbar
668
+ * --------------------------------------------------------------------- */
669
+
670
+ nav.sidebar::-webkit-scrollbar,
671
+ .inset pre::-webkit-scrollbar,
672
+ .console pre::-webkit-scrollbar,
673
+ .code::-webkit-scrollbar {
674
+ width: 10px;
675
+ height: 10px;
676
+ }
677
+
678
+ .inset pre::-webkit-scrollbar-thumb,
679
+ .console pre::-webkit-scrollbar-thumb,
680
+ .code::-webkit-scrollbar-thumb {
681
+ background: #ccc;
682
+ border-radius: 5px;
683
+ }
684
+
685
+ nav.sidebar::-webkit-scrollbar-thumb {
686
+ background: rgba(0, 0, 0, 0.0);
687
+ border-radius: 5px;
688
+ }
689
+
690
+ nav.sidebar:hover::-webkit-scrollbar-thumb {
691
+ background-color: #999;
692
+ background: -webkit-linear-gradient(left, #aaa, #999);
693
+ }
694
+
695
+ .console pre:hover::-webkit-scrollbar-thumb,
696
+ .inset pre:hover::-webkit-scrollbar-thumb,
697
+ .code:hover::-webkit-scrollbar-thumb {
698
+ background: #888;
699
+ }
700
+ </style>
701
+
702
+ <%# IE8 compatibility crap %>
703
+ <script>
704
+ (function() {
705
+ var elements = ["section", "nav", "header", "footer", "audio"];
706
+ for (var i = 0; i < elements.length; i++) {
707
+ document.createElement(elements[i]);
708
+ }
709
+ })();
710
+ </script>
711
+
712
+ <%#
713
+ If Rails's Turbolinks is used, the Better Errors page is probably
714
+ rendered in the host app's layout. Let's empty out the styles of the
715
+ host app.
716
+ %>
717
+ <script>
718
+ if (window.Turbolinks) {
719
+ for(var i=0; i < document.styleSheets.length; i++) {
720
+ if(document.styleSheets[i].href)
721
+ document.styleSheets[i].disabled = true;
722
+ }
723
+ document.addEventListener("page:restore", function restoreCSS(e) {
724
+ for(var i=0; i < document.styleSheets.length; i++) {
725
+ document.styleSheets[i].disabled = false;
726
+ }
727
+ document.removeEventListener("page:restore", restoreCSS, false);
728
+ });
729
+ }
730
+ </script>
731
+
732
+ <div class='top'>
733
+ <header class="exception">
734
+ <h2><strong><%= exception_type %></strong> <span>at <%= request_path %></span></h2>
735
+ <p><%= exception_message %></p>
736
+ </header>
737
+ </div>
738
+
739
+ <section class="backtrace">
740
+ <nav class="sidebar">
741
+ <nav class="tabs">
742
+ <a href="#" id="application_frames">Application Frames</a>
743
+ <a href="#" id="all_frames">All Frames</a>
744
+ </nav>
745
+ <ul class="frames">
746
+ <% backtrace_frames.each_with_index do |frame, index| %>
747
+ <li class="<%= frame.context %>" data-context="<%= frame.context %>" data-index="<%= index %>">
748
+ <span class='stroke'></span>
749
+ <i class="icon <%= frame.context %>"></i>
750
+ <div class="info">
751
+ <div class="name">
752
+ <strong><%= frame.class_name %></strong><span class='method'><%= frame.method_name %></span>
753
+ </div>
754
+ <div class="location">
755
+ <span class="filename"><%= frame.pretty_path %></span>, line <span class="line"><%= frame.line %></span>
756
+ </div>
757
+ </div>
758
+ </li>
759
+ <% end %>
760
+ </ul>
761
+ </nav>
762
+
763
+ <% backtrace_frames.each_with_index do |frame, index| %>
764
+ <div class="frame_info" id="frame_info_<%= index %>" style="display:none;"></div>
765
+ <% end %>
766
+ </section>
767
+ </body>
768
+ <script>
769
+ (function() {
770
+
771
+ var OID = "<%= id %>";
772
+
773
+ var previousFrame = null;
774
+ var previousFrameInfo = null;
775
+ var allFrames = document.querySelectorAll("ul.frames li");
776
+ var allFrameInfos = document.querySelectorAll(".frame_info");
777
+
778
+ function apiCall(method, opts, cb) {
779
+ var req = new XMLHttpRequest();
780
+ req.open("POST", <%== uri_prefix.gsub("<", "&lt;").inspect %> + "/__better_errors/" + OID + "/" + method, true);
781
+ req.setRequestHeader("Content-Type", "application/json");
782
+ req.send(JSON.stringify(opts));
783
+ req.onreadystatechange = function() {
784
+ if(req.readyState == 4) {
785
+ var res = JSON.parse(req.responseText);
786
+ cb(res);
787
+ }
788
+ };
789
+ }
790
+
791
+ function escapeHTML(html) {
792
+ return html.replace(/&/, "&amp;").replace(/</g, "&lt;");
793
+ }
794
+
795
+ function REPL(index) {
796
+ this.index = index;
797
+
798
+ var previousCommands = JSON.parse(localStorage.getItem("better_errors_previous_commands"));
799
+ if(previousCommands === null) {
800
+ localStorage.setItem("better_errors_previous_commands", JSON.stringify([]));
801
+ previousCommands = [];
802
+ }
803
+
804
+ this.previousCommandOffset = previousCommands.length;
805
+ }
806
+
807
+ REPL.all = [];
808
+
809
+ REPL.prototype.install = function(containerElement) {
810
+ this.container = containerElement;
811
+
812
+ this.promptElement = this.container.querySelector(".prompt span");
813
+ this.inputElement = this.container.querySelector("input");
814
+ this.outputElement = this.container.querySelector("pre");
815
+
816
+ var self = this;
817
+ this.inputElement.onkeydown = function(ev) {
818
+ self.onKeyDown(ev);
819
+ };
820
+
821
+ this.setPrompt(">>");
822
+
823
+ REPL.all[this.index] = this;
824
+ }
825
+
826
+ REPL.prototype.focus = function() {
827
+ this.inputElement.focus();
828
+ };
829
+
830
+ REPL.prototype.setPrompt = function(prompt) {
831
+ this._prompt = prompt;
832
+ this.promptElement.innerHTML = escapeHTML(prompt);
833
+ };
834
+
835
+ REPL.prototype.getInput = function() {
836
+ return this.inputElement.value;
837
+ };
838
+
839
+ REPL.prototype.setInput = function(text) {
840
+ this.inputElement.value = text;
841
+
842
+ if(this.inputElement.setSelectionRange) {
843
+ // set cursor to end of input
844
+ this.inputElement.setSelectionRange(text.length, text.length);
845
+ }
846
+ };
847
+
848
+ REPL.prototype.writeRawOutput = function(output) {
849
+ this.outputElement.innerHTML += output;
850
+ this.outputElement.scrollTop = this.outputElement.scrollHeight;
851
+ };
852
+
853
+ REPL.prototype.writeOutput = function(output) {
854
+ this.writeRawOutput(escapeHTML(output));
855
+ };
856
+
857
+ REPL.prototype.sendInput = function(line) {
858
+ var self = this;
859
+ apiCall("eval", { "index": this.index, source: line }, function(response) {
860
+ if(response.error) {
861
+ self.writeOutput(response.error + "\n");
862
+ }
863
+ self.writeOutput(self._prompt + " ");
864
+ self.writeRawOutput(response.highlighted_input + "\n");
865
+ self.writeOutput(response.result);
866
+ self.setPrompt(response.prompt);
867
+ self.setInput(response.prefilled_input);
868
+ });
869
+ };
870
+
871
+ REPL.prototype.onEnterKey = function() {
872
+ var text = this.getInput();
873
+ if(text != "" && text !== undefined) {
874
+ var previousCommands = JSON.parse(localStorage.getItem("better_errors_previous_commands"));
875
+ this.previousCommandOffset = previousCommands.push(text);
876
+ if(previousCommands.length > 100) {
877
+ previousCommands.splice(0, 1);
878
+ this.previousCommandOffset -= 1;
879
+ }
880
+ localStorage.setItem("better_errors_previous_commands", JSON.stringify(previousCommands));
881
+ }
882
+ this.setInput("");
883
+ this.sendInput(text);
884
+ };
885
+
886
+ REPL.prototype.onNavigateHistory = function(direction) {
887
+ this.previousCommandOffset += direction;
888
+ var previousCommands = JSON.parse(localStorage.getItem("better_errors_previous_commands"));
889
+
890
+ if(this.previousCommandOffset < 0) {
891
+ this.previousCommandOffset = -1;
892
+ this.setInput("");
893
+ return;
894
+ }
895
+
896
+ if(this.previousCommandOffset >= previousCommands.length) {
897
+ this.previousCommandOffset = previousCommands.length;
898
+ this.setInput("");
899
+ return;
900
+ }
901
+
902
+ this.setInput(previousCommands[this.previousCommandOffset]);
903
+ };
904
+
905
+ REPL.prototype.onKeyDown = function(ev) {
906
+ if(ev.keyCode == 13) {
907
+ this.onEnterKey();
908
+ } else if(ev.keyCode == 38 || (ev.ctrlKey && ev.keyCode == 80)) {
909
+ // the user pressed the up arrow or Ctrl-P
910
+ this.onNavigateHistory(-1);
911
+ ev.preventDefault();
912
+ return false;
913
+ } else if(ev.keyCode == 40 || (ev.ctrlKey && ev.keyCode == 78)) {
914
+ // the user pressed the down arrow or Ctrl-N
915
+ this.onNavigateHistory(1);
916
+ ev.preventDefault();
917
+ return false;
918
+ }
919
+ };
920
+
921
+ function switchTo(el) {
922
+ if(previousFrameInfo) previousFrameInfo.style.display = "none";
923
+ previousFrameInfo = el;
924
+
925
+ el.style.display = "block";
926
+
927
+ var replInput = el.querySelector('.console input');
928
+ if (replInput) replInput.focus();
929
+ }
930
+
931
+ function selectFrameInfo(index) {
932
+ var el = allFrameInfos[index];
933
+ if(el) {
934
+ if (el.loaded) {
935
+ return switchTo(el);
936
+ }
937
+
938
+ apiCall("variables", { "index": index }, function(response) {
939
+ el.loaded = true;
940
+ if(response.error) {
941
+ el.innerHTML = "<span class='error'>" + escapeHTML(response.error) + "</span>";
942
+ } else {
943
+ el.innerHTML = response.html;
944
+
945
+ var repl = el.querySelector(".repl .console");
946
+ if(repl) {
947
+ new REPL(index).install(repl);
948
+ }
949
+
950
+ switchTo(el);
951
+ }
952
+ });
953
+ }
954
+ }
955
+
956
+ for(var i = 0; i < allFrames.length; i++) {
957
+ (function(i, el) {
958
+ var el = allFrames[i];
959
+ el.onclick = function() {
960
+ if(previousFrame) {
961
+ previousFrame.className = "";
962
+ }
963
+ el.className = "selected";
964
+ previousFrame = el;
965
+
966
+ selectFrameInfo(el.attributes["data-index"].value);
967
+ };
968
+ })(i);
969
+ }
970
+
971
+ // Click the first application frame
972
+ (
973
+ document.querySelector(".frames li.application") ||
974
+ document.querySelector(".frames li")
975
+ ).onclick();
976
+
977
+ // This is the second query performed for frames; maybe the 'allFrames' list
978
+ // currently used and this list can be better used to avoid the repetition:
979
+ var applicationFramesCount = document.querySelectorAll(
980
+ "ul.frames li[data-context=application]"
981
+ ).length;
982
+
983
+ var applicationFramesButtonIsInstalled = false;
984
+ var applicationFramesButton = document.getElementById("application_frames");
985
+ var allFramesButton = document.getElementById("all_frames");
986
+
987
+ // The application frames button only needs to be bound if
988
+ // there are actually any application frames to look at.
989
+ var installApplicationFramesButton = function() {
990
+ applicationFramesButton.onclick = function() {
991
+ allFramesButton.className = "";
992
+ applicationFramesButton.className = "selected";
993
+ for(var i = 0; i < allFrames.length; i++) {
994
+ if(allFrames[i].attributes["data-context"].value == "application") {
995
+ allFrames[i].style.display = "block";
996
+ } else {
997
+ allFrames[i].style.display = "none";
998
+ }
999
+ }
1000
+ return false;
1001
+ };
1002
+
1003
+ applicationFramesButtonIsInstalled = true;
1004
+ }
1005
+
1006
+ allFramesButton.onclick = function() {
1007
+ if(applicationFramesButtonIsInstalled) {
1008
+ applicationFramesButton.className = "";
1009
+ }
1010
+
1011
+ allFramesButton.className = "selected";
1012
+ for(var i = 0; i < allFrames.length; i++) {
1013
+ allFrames[i].style.display = "block";
1014
+ }
1015
+ return false;
1016
+ };
1017
+
1018
+ // If there are no application frames, select the 'All Frames'
1019
+ // tab by default.
1020
+ if(applicationFramesCount > 0) {
1021
+ installApplicationFramesButton();
1022
+ applicationFramesButton.onclick();
1023
+ } else {
1024
+ applicationFramesButton.className = "disabled";
1025
+ applicationFramesButton.title = "No application frames available";
1026
+ allFramesButton.onclick();
1027
+ }
1028
+ })();
1029
+ </script>
1030
+ </html>
1031
+
1032
+ <!-- generated by Better Errors in <%= Time.now.to_f - @start_time %> seconds -->