better_errors 2.1.1 → 2.2.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.
Potentially problematic release.
This version of better_errors might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +5 -2
- data/Gemfile +3 -2
- data/LICENSE.txt +1 -1
- data/README.md +13 -1
- data/feature-screenshots/1-application-error.jpg +0 -0
- data/feature-screenshots/2-other-application-frame.jpg +0 -0
- data/feature-screenshots/3-live-shell.jpg +0 -0
- data/feature-screenshots/4-other-frames.jpg +0 -0
- data/feature-screenshots/5-open-editor.jpg +0 -0
- data/feature-screenshots/6-local-variables.jpg +0 -0
- data/feature-screenshots/7-non-html-requests.jpg +0 -0
- data/feature-screenshots/8-xhr-shows-text-error.jpg +0 -0
- data/feature-screenshots/9-xhr-error-in-manual-console.jpg +0 -0
- data/lib/better_errors/error_page.rb +8 -0
- data/lib/better_errors/middleware.rb +2 -2
- data/lib/better_errors/raised_exception.rb +3 -1
- data/lib/better_errors/templates/main.erb +3 -3
- data/lib/better_errors/templates/text.erb +2 -2
- data/lib/better_errors/templates/variable_info.erb +21 -19
- data/lib/better_errors/version.rb +1 -1
- data/spec/better_errors/code_formatter_spec.rb +13 -13
- data/spec/better_errors/error_page_spec.rb +35 -19
- data/spec/better_errors/middleware_spec.rb +74 -40
- data/spec/better_errors/raised_exception_spec.rb +7 -6
- data/spec/better_errors/repl/pry_spec.rb +10 -10
- data/spec/better_errors/repl/shared_examples.rb +5 -5
- data/spec/better_errors/stack_frame_spec.rb +37 -37
- data/spec/better_errors_spec.rb +11 -11
- metadata +12 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d53ee50b3a870061cf9298e5225635771043d6d7
|
4
|
+
data.tar.gz: b4280966bd31d24f4f4cb12ea54c1eea11bccdaa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e068142eaef86037e0cc6088672158a0a9d021e49e7fe9a823c888d1f319181e07b18a03852b46da4a6d9a0db49f6d420ac7359ceb6ec4a35549bb56b403bec5
|
7
|
+
data.tar.gz: 9c184752196c2cf65b7dba20f94cf3b47428c6fd067c880ffa452bca8ed2fa6d3e84e5cd64ae6e295199622fe4e1ff2d6d09979d2a35e0b9bf36d38c6ebfdd33
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -6,10 +6,14 @@ Better Errors replaces the standard Rails error page with a much better and more
|
|
6
6
|
|
7
7
|
## Features
|
8
8
|
|
9
|
+
For screenshots of these features, [see the wiki](https://github.com/charliesome/better_errors/wiki).
|
10
|
+
|
9
11
|
* Full stack trace
|
10
12
|
* Source code inspection for all stack frames (with highlighting)
|
11
13
|
* Local and instance variable inspection
|
12
|
-
* Live REPL on every stack frame
|
14
|
+
* Live shell (REPL) on every stack frame
|
15
|
+
* Links directly to the source line in your editor
|
16
|
+
* Useful information in non-HTML requests
|
13
17
|
|
14
18
|
## Installation
|
15
19
|
|
@@ -95,6 +99,14 @@ in `development`. Another option would be to use Webrick, Mongrel, Thin,
|
|
95
99
|
or another single-process server as your `rails server`, when you are trying
|
96
100
|
to troubleshoot an issue in development.
|
97
101
|
|
102
|
+
##Specify editor to open files in
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
# e.g. in config/initializers/better_errors.rb
|
106
|
+
# Other preset values are [:mvim, :macvim, :textmate, :txmt, :tm, :sublime, :subl, :st]
|
107
|
+
BetterErrors.editor = :mvim
|
108
|
+
```
|
109
|
+
|
98
110
|
## Get in touch!
|
99
111
|
|
100
112
|
If you're using better_errors, I'd love to hear from you. Drop me a line and tell me what you think!
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -100,7 +100,7 @@ module BetterErrors
|
|
100
100
|
end
|
101
101
|
|
102
102
|
status_code = 500
|
103
|
-
if defined?
|
103
|
+
if defined?(ActionDispatch::ExceptionWrapper) && exception
|
104
104
|
status_code = ActionDispatch::ExceptionWrapper.new(env, exception).status_code
|
105
105
|
end
|
106
106
|
|
@@ -115,7 +115,7 @@ module BetterErrors
|
|
115
115
|
def log_exception
|
116
116
|
return unless BetterErrors.logger
|
117
117
|
|
118
|
-
message = "\n#{@error_page.
|
118
|
+
message = "\n#{@error_page.exception_type} - #{@error_page.exception_message}:\n"
|
119
119
|
@error_page.backtrace_frames.each do |frame|
|
120
120
|
message << " #{frame}\n"
|
121
121
|
end
|
@@ -4,7 +4,9 @@ module BetterErrors
|
|
4
4
|
attr_reader :exception, :message, :backtrace
|
5
5
|
|
6
6
|
def initialize(exception)
|
7
|
-
if exception.respond_to?(:
|
7
|
+
if exception.respond_to?(:cause)
|
8
|
+
exception = exception.cause if exception.cause
|
9
|
+
elsif exception.respond_to?(:original_exception) && exception.original_exception
|
8
10
|
exception = exception.original_exception
|
9
11
|
end
|
10
12
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html>
|
3
3
|
<head>
|
4
|
-
<title><%=
|
4
|
+
<title><%= exception_type %> at <%= request_path %></title>
|
5
5
|
</head>
|
6
6
|
<body>
|
7
7
|
<%# Stylesheets are placed in the <body> for Turbolinks compatibility. %>
|
@@ -731,8 +731,8 @@
|
|
731
731
|
|
732
732
|
<div class='top'>
|
733
733
|
<header class="exception">
|
734
|
-
<h2><strong><%=
|
735
|
-
<p><%=
|
734
|
+
<h2><strong><%= exception_type %></strong> <span>at <%= request_path %></span></h2>
|
735
|
+
<p><%= exception_message %></p>
|
736
736
|
</header>
|
737
737
|
</div>
|
738
738
|
|
@@ -1,6 +1,6 @@
|
|
1
|
-
<%== text_heading("=", "%s at %s" % [
|
1
|
+
<%== text_heading("=", "%s at %s" % [exception_type, request_path]) %>
|
2
2
|
|
3
|
-
> <%==
|
3
|
+
> <%== exception_message %>
|
4
4
|
<% if backtrace_frames.any? %>
|
5
5
|
|
6
6
|
<%== text_heading("-", "%s, line %i" % [first_frame.pretty_path, first_frame.line]) %>
|
@@ -45,26 +45,28 @@
|
|
45
45
|
</div>
|
46
46
|
</div>
|
47
47
|
|
48
|
-
|
49
|
-
<
|
50
|
-
|
51
|
-
<
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
48
|
+
<% if BetterErrors.binding_of_caller_available? && @frame.frame_binding %>
|
49
|
+
<div class="sub">
|
50
|
+
<h3>Local Variables</h3>
|
51
|
+
<div class='inset variables'>
|
52
|
+
<table class="var_table">
|
53
|
+
<% @frame.local_variables.each do |name, value| %>
|
54
|
+
<tr><td class="name"><%= name %></td><td><pre><%== inspect_value value %></pre></td></tr>
|
55
|
+
<% end %>
|
56
|
+
</table>
|
57
|
+
</div>
|
56
58
|
</div>
|
57
|
-
</div>
|
58
59
|
|
59
|
-
<div class="sub">
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
60
|
+
<div class="sub">
|
61
|
+
<h3>Instance Variables</h3>
|
62
|
+
<div class="inset variables">
|
63
|
+
<table class="var_table">
|
64
|
+
<% @frame.instance_variables.each do |name, value| %>
|
65
|
+
<tr><td class="name"><%= name %></td><td><pre><%== inspect_value value %></pre></td></tr>
|
66
|
+
<% end %>
|
67
|
+
</table>
|
68
|
+
</div>
|
67
69
|
</div>
|
68
|
-
</div>
|
69
70
|
|
70
|
-
<!-- <%= Time.now.to_f - @var_start_time %> seconds -->
|
71
|
+
<!-- <%= Time.now.to_f - @var_start_time %> seconds -->
|
72
|
+
<% end %>
|
@@ -7,13 +7,13 @@ module BetterErrors
|
|
7
7
|
let(:formatter) { CodeFormatter.new(filename, 8) }
|
8
8
|
|
9
9
|
it "picks an appropriate scanner" do
|
10
|
-
formatter.coderay_scanner.
|
10
|
+
expect(formatter.coderay_scanner).to eq(:ruby)
|
11
11
|
end
|
12
12
|
|
13
13
|
it "shows 5 lines of context" do
|
14
|
-
formatter.line_range.
|
14
|
+
expect(formatter.line_range).to eq(3..13)
|
15
15
|
|
16
|
-
formatter.context_lines.
|
16
|
+
expect(formatter.context_lines).to eq([
|
17
17
|
"three\n",
|
18
18
|
"four\n",
|
19
19
|
"five\n",
|
@@ -25,40 +25,40 @@ module BetterErrors
|
|
25
25
|
"eleven\n",
|
26
26
|
"twelve\n",
|
27
27
|
"thirteen\n"
|
28
|
-
]
|
28
|
+
])
|
29
29
|
end
|
30
30
|
|
31
31
|
it "works when the line is right on the edge" do
|
32
32
|
formatter = CodeFormatter.new(filename, 20)
|
33
|
-
formatter.line_range.
|
33
|
+
expect(formatter.line_range).to eq(15..20)
|
34
34
|
end
|
35
35
|
|
36
36
|
describe CodeFormatter::HTML do
|
37
37
|
it "highlights the erroring line" do
|
38
38
|
formatter = CodeFormatter::HTML.new(filename, 8)
|
39
|
-
formatter.output.
|
39
|
+
expect(formatter.output).to match(/highlight.*eight/)
|
40
40
|
end
|
41
41
|
|
42
42
|
it "works when the line is right on the edge" do
|
43
43
|
formatter = CodeFormatter::HTML.new(filename, 20)
|
44
|
-
formatter.output.
|
44
|
+
expect(formatter.output).not_to eq(formatter.source_unavailable)
|
45
45
|
end
|
46
46
|
|
47
47
|
it "doesn't barf when the lines don't make any sense" do
|
48
48
|
formatter = CodeFormatter::HTML.new(filename, 999)
|
49
|
-
formatter.output.
|
49
|
+
expect(formatter.output).to eq(formatter.source_unavailable)
|
50
50
|
end
|
51
51
|
|
52
52
|
it "doesn't barf when the file doesn't exist" do
|
53
53
|
formatter = CodeFormatter::HTML.new("fkdguhskd7e l", 1)
|
54
|
-
formatter.output.
|
54
|
+
expect(formatter.output).to eq(formatter.source_unavailable)
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
58
|
describe CodeFormatter::Text do
|
59
59
|
it "highlights the erroring line" do
|
60
60
|
formatter = CodeFormatter::Text.new(filename, 8)
|
61
|
-
formatter.output.
|
61
|
+
expect(formatter.output).to eq <<-TEXT.gsub(/^ /, "")
|
62
62
|
3 three
|
63
63
|
4 four
|
64
64
|
5 five
|
@@ -75,17 +75,17 @@ module BetterErrors
|
|
75
75
|
|
76
76
|
it "works when the line is right on the edge" do
|
77
77
|
formatter = CodeFormatter::Text.new(filename, 20)
|
78
|
-
formatter.output.
|
78
|
+
expect(formatter.output).not_to eq(formatter.source_unavailable)
|
79
79
|
end
|
80
80
|
|
81
81
|
it "doesn't barf when the lines don't make any sense" do
|
82
82
|
formatter = CodeFormatter::Text.new(filename, 999)
|
83
|
-
formatter.output.
|
83
|
+
expect(formatter.output).to eq(formatter.source_unavailable)
|
84
84
|
end
|
85
85
|
|
86
86
|
it "doesn't barf when the file doesn't exist" do
|
87
87
|
formatter = CodeFormatter::Text.new("fkdguhskd7e l", 1)
|
88
|
-
formatter.output.
|
88
|
+
expect(formatter.output).to eq(formatter.source_unavailable)
|
89
89
|
end
|
90
90
|
end
|
91
91
|
end
|
@@ -19,15 +19,15 @@ module BetterErrors
|
|
19
19
|
}
|
20
20
|
|
21
21
|
it "includes the error message" do
|
22
|
-
response.
|
22
|
+
expect(response).to include("you divided by zero you silly goose!")
|
23
23
|
end
|
24
24
|
|
25
25
|
it "includes the request path" do
|
26
|
-
response.
|
26
|
+
expect(response).to include("/some/path")
|
27
27
|
end
|
28
28
|
|
29
29
|
it "includes the exception class" do
|
30
|
-
response.
|
30
|
+
expect(response).to include("ZeroDivisionError")
|
31
31
|
end
|
32
32
|
|
33
33
|
context "variable inspection" do
|
@@ -36,41 +36,57 @@ module BetterErrors
|
|
36
36
|
if BetterErrors.binding_of_caller_available?
|
37
37
|
it "shows local variables" do
|
38
38
|
html = error_page.do_variables("index" => 0)[:html]
|
39
|
-
html.
|
40
|
-
html.
|
41
|
-
html.
|
42
|
-
html.
|
39
|
+
expect(html).to include("local_a")
|
40
|
+
expect(html).to include(":value_for_local_a")
|
41
|
+
expect(html).to include("local_b")
|
42
|
+
expect(html).to include(":value_for_local_b")
|
43
43
|
end
|
44
44
|
else
|
45
45
|
it "tells the user to add binding_of_caller to their gemfile to get fancy features" do
|
46
46
|
html = error_page.do_variables("index" => 0)[:html]
|
47
|
-
html.
|
47
|
+
expect(html).to include(%{gem "binding_of_caller"})
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
51
|
it "shows instance variables" do
|
52
52
|
html = error_page.do_variables("index" => 0)[:html]
|
53
|
-
html.
|
54
|
-
html.
|
55
|
-
html.
|
56
|
-
html.
|
53
|
+
expect(html).to include("inst_c")
|
54
|
+
expect(html).to include(":value_for_inst_c")
|
55
|
+
expect(html).to include("inst_d")
|
56
|
+
expect(html).to include(":value_for_inst_d")
|
57
57
|
end
|
58
58
|
|
59
59
|
it "shows filter instance variables" do
|
60
|
-
BetterErrors.
|
60
|
+
allow(BetterErrors).to receive(:ignored_instance_variables).and_return([ :@inst_d ])
|
61
61
|
html = error_page.do_variables("index" => 0)[:html]
|
62
|
-
html.
|
63
|
-
html.
|
64
|
-
html.
|
65
|
-
html.
|
62
|
+
expect(html).to include("inst_c")
|
63
|
+
expect(html).to include(":value_for_inst_c")
|
64
|
+
expect(html).not_to include('<td class="name">@inst_d</td>')
|
65
|
+
expect(html).not_to include("<pre>:value_for_inst_d</pre>")
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
69
|
it "doesn't die if the source file is not a real filename" do
|
70
|
-
exception.
|
70
|
+
allow(exception).to receive(:backtrace).and_return([
|
71
71
|
"<internal:prelude>:10:in `spawn_rack_application'"
|
72
72
|
])
|
73
|
-
response.
|
73
|
+
expect(response).to include("Source unavailable")
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'with an exception with blank lines' do
|
77
|
+
class SpacedError < StandardError
|
78
|
+
def initialize(message = nil)
|
79
|
+
message = "\n\n#{message}" if message
|
80
|
+
super
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
let!(:exception) { raise SpacedError, "Danger Warning!" rescue $! }
|
85
|
+
|
86
|
+
it 'should not include leading blank lines from exception_message' do
|
87
|
+
expect(exception.message).to match(/\A\n\n/)
|
88
|
+
expect(error_page.exception_message).not_to match(/\A\n\n/)
|
89
|
+
end
|
74
90
|
end
|
75
91
|
end
|
76
92
|
end
|
@@ -6,42 +6,42 @@ module BetterErrors
|
|
6
6
|
let(:exception) { RuntimeError.new("oh no :(") }
|
7
7
|
|
8
8
|
it "passes non-error responses through" do
|
9
|
-
app.call({}).
|
9
|
+
expect(app.call({})).to eq(":)")
|
10
10
|
end
|
11
11
|
|
12
12
|
it "calls the internal methods" do
|
13
|
-
app.
|
13
|
+
expect(app).to receive :internal_call
|
14
14
|
app.call("PATH_INFO" => "/__better_errors/1/preform_awesomness")
|
15
15
|
end
|
16
16
|
|
17
17
|
it "calls the internal methods on any subfolder path" do
|
18
|
-
app.
|
18
|
+
expect(app).to receive :internal_call
|
19
19
|
app.call("PATH_INFO" => "/any_sub/folder/path/__better_errors/1/preform_awesomness")
|
20
20
|
end
|
21
21
|
|
22
22
|
it "shows the error page" do
|
23
|
-
app.
|
23
|
+
expect(app).to receive :show_error_page
|
24
24
|
app.call("PATH_INFO" => "/__better_errors/")
|
25
25
|
end
|
26
26
|
|
27
27
|
it "shows the error page on any subfolder path" do
|
28
|
-
app.
|
28
|
+
expect(app).to receive :show_error_page
|
29
29
|
app.call("PATH_INFO" => "/any_sub/folder/path/__better_errors/")
|
30
30
|
end
|
31
31
|
|
32
32
|
it "doesn't show the error page to a non-local address" do
|
33
|
-
app.
|
33
|
+
expect(app).not_to receive :better_errors_call
|
34
34
|
app.call("REMOTE_ADDR" => "1.2.3.4")
|
35
35
|
end
|
36
36
|
|
37
37
|
it "shows to a whitelisted IP" do
|
38
38
|
BetterErrors::Middleware.allow_ip! '77.55.33.11'
|
39
|
-
app.
|
39
|
+
expect(app).to receive :better_errors_call
|
40
40
|
app.call("REMOTE_ADDR" => "77.55.33.11")
|
41
41
|
end
|
42
42
|
|
43
43
|
it "respects the X-Forwarded-For header" do
|
44
|
-
app.
|
44
|
+
expect(app).not_to receive :better_errors_call
|
45
45
|
app.call(
|
46
46
|
"REMOTE_ADDR" => "127.0.0.1",
|
47
47
|
"HTTP_X_FORWARDED_FOR" => "1.2.3.4",
|
@@ -61,12 +61,20 @@ module BetterErrors
|
|
61
61
|
|
62
62
|
it "shows that no errors have been recorded" do
|
63
63
|
status, headers, body = app.call("PATH_INFO" => "/__better_errors")
|
64
|
-
body.join.
|
64
|
+
expect(body.join).to match /No errors have been recorded yet./
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'does not attempt to use ActionDispatch::ExceptionWrapper with a nil exception' do
|
68
|
+
ad_ew = double("ActionDispatch::ExceptionWrapper")
|
69
|
+
stub_const('ActionDispatch::ExceptionWrapper', ad_ew)
|
70
|
+
ad_ew.should_not_receive :new
|
71
|
+
|
72
|
+
status, headers, body = app.call("PATH_INFO" => "/__better_errors")
|
65
73
|
end
|
66
74
|
|
67
75
|
it "shows that no errors have been recorded on any subfolder path" do
|
68
76
|
status, headers, body = app.call("PATH_INFO" => "/any_sub/folder/path/__better_errors")
|
69
|
-
body.join.
|
77
|
+
expect(body.join).to match /No errors have been recorded yet./
|
70
78
|
end
|
71
79
|
end
|
72
80
|
|
@@ -76,76 +84,102 @@ module BetterErrors
|
|
76
84
|
it "returns status 500" do
|
77
85
|
status, headers, body = app.call({})
|
78
86
|
|
79
|
-
status.
|
87
|
+
expect(status).to eq(500)
|
80
88
|
end
|
81
89
|
|
82
|
-
|
83
|
-
|
84
|
-
|
90
|
+
if Exception.new.respond_to?(:cause)
|
91
|
+
context "cause" do
|
92
|
+
class OtherException < Exception
|
93
|
+
def initialize(message)
|
94
|
+
super(message)
|
95
|
+
end
|
96
|
+
end
|
85
97
|
|
86
|
-
|
87
|
-
|
88
|
-
|
98
|
+
it "shows Original Exception if it responds_to and has an cause" do
|
99
|
+
app = Middleware.new(->env {
|
100
|
+
begin
|
101
|
+
raise "Original Exception"
|
102
|
+
rescue
|
103
|
+
raise OtherException.new("Other Exception")
|
104
|
+
end
|
105
|
+
})
|
106
|
+
|
107
|
+
status, _, body = app.call({})
|
108
|
+
|
109
|
+
expect(status).to eq(500)
|
110
|
+
expect(body.join).not_to match(/\n> Other Exception\n/)
|
111
|
+
expect(body.join).to match(/\n> Original Exception\n/)
|
89
112
|
end
|
90
113
|
end
|
114
|
+
else
|
115
|
+
context "original_exception" do
|
116
|
+
class OriginalExceptionException < Exception
|
117
|
+
attr_reader :original_exception
|
118
|
+
|
119
|
+
def initialize(message, original_exception = nil)
|
120
|
+
super(message)
|
121
|
+
@original_exception = original_exception
|
122
|
+
end
|
123
|
+
end
|
91
124
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
125
|
+
it "shows Original Exception if it responds_to and has an original_exception" do
|
126
|
+
app = Middleware.new(->env {
|
127
|
+
raise OriginalExceptionException.new("Other Exception", Exception.new("Original Exception"))
|
128
|
+
})
|
96
129
|
|
97
|
-
|
130
|
+
status, _, body = app.call({})
|
98
131
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
132
|
+
expect(status).to eq(500)
|
133
|
+
expect(body.join).not_to match(/Other Exception/)
|
134
|
+
expect(body.join).to match(/Original Exception/)
|
135
|
+
end
|
103
136
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
137
|
+
it "won't crash if the exception responds_to but doesn't have an original_exception" do
|
138
|
+
app = Middleware.new(->env {
|
139
|
+
raise OriginalExceptionException.new("Other Exception")
|
140
|
+
})
|
108
141
|
|
109
|
-
|
142
|
+
status, _, body = app.call({})
|
110
143
|
|
111
|
-
|
112
|
-
|
144
|
+
expect(status).to eq(500)
|
145
|
+
expect(body.join).to match(/Other Exception/)
|
146
|
+
end
|
113
147
|
end
|
114
148
|
end
|
115
149
|
|
116
150
|
it "returns ExceptionWrapper's status_code" do
|
117
151
|
ad_ew = double("ActionDispatch::ExceptionWrapper")
|
118
|
-
ad_ew.
|
152
|
+
allow(ad_ew).to receive('new').with({}, exception) { double("ExceptionWrapper", status_code: 404) }
|
119
153
|
stub_const('ActionDispatch::ExceptionWrapper', ad_ew)
|
120
154
|
|
121
155
|
status, headers, body = app.call({})
|
122
156
|
|
123
|
-
status.
|
157
|
+
expect(status).to eq(404)
|
124
158
|
end
|
125
159
|
|
126
160
|
it "returns UTF-8 error pages" do
|
127
161
|
status, headers, body = app.call({})
|
128
162
|
|
129
|
-
headers["Content-Type"].
|
163
|
+
expect(headers["Content-Type"]).to match /charset=utf-8/
|
130
164
|
end
|
131
165
|
|
132
166
|
it "returns text pages by default" do
|
133
167
|
status, headers, body = app.call({})
|
134
168
|
|
135
|
-
headers["Content-Type"].
|
169
|
+
expect(headers["Content-Type"]).to match /text\/plain/
|
136
170
|
end
|
137
171
|
|
138
172
|
it "returns HTML pages by default" do
|
139
173
|
# Chrome's 'Accept' header looks similar this.
|
140
174
|
status, headers, body = app.call("HTTP_ACCEPT" => "text/html,application/xhtml+xml;q=0.9,*/*")
|
141
175
|
|
142
|
-
headers["Content-Type"].
|
176
|
+
expect(headers["Content-Type"]).to match /text\/html/
|
143
177
|
end
|
144
178
|
|
145
179
|
it "logs the exception" do
|
146
180
|
logger = Object.new
|
147
|
-
logger.
|
148
|
-
BetterErrors.
|
181
|
+
expect(logger).to receive :fatal
|
182
|
+
allow(BetterErrors).to receive(:logger).and_return(logger)
|
149
183
|
|
150
184
|
app.call({})
|
151
185
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "spec_helper"
|
2
|
+
require "rspec/its"
|
2
3
|
|
3
4
|
module BetterErrors
|
4
5
|
describe RaisedException do
|
@@ -24,8 +25,8 @@ module BetterErrors
|
|
24
25
|
its(:type) { should == SyntaxError }
|
25
26
|
|
26
27
|
it "has the right filename and line number in the backtrace" do
|
27
|
-
subject.backtrace.first.filename.
|
28
|
-
subject.backtrace.first.line.
|
28
|
+
expect(subject.backtrace.first.filename).to eq("foo.rb")
|
29
|
+
expect(subject.backtrace.first.line).to eq(123)
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
@@ -44,8 +45,8 @@ module BetterErrors
|
|
44
45
|
its(:type) { should == Haml::SyntaxError }
|
45
46
|
|
46
47
|
it "has the right filename and line number in the backtrace" do
|
47
|
-
subject.backtrace.first.filename.
|
48
|
-
subject.backtrace.first.line.
|
48
|
+
expect(subject.backtrace.first.filename).to eq("foo.rb")
|
49
|
+
expect(subject.backtrace.first.line).to eq(123)
|
49
50
|
end
|
50
51
|
end
|
51
52
|
|
@@ -64,8 +65,8 @@ module BetterErrors
|
|
64
65
|
its(:type) { should == Sprockets::Coffeelint::Error }
|
65
66
|
|
66
67
|
it "has the right filename and line number in the backtrace" do
|
67
|
-
subject.backtrace.first.filename.
|
68
|
-
subject.backtrace.first.line.
|
68
|
+
expect(subject.backtrace.first.filename).to eq("app/assets/javascripts/files/index.coffee")
|
69
|
+
expect(subject.backtrace.first.line).to eq(11)
|
69
70
|
end
|
70
71
|
end
|
71
72
|
end
|
@@ -15,23 +15,23 @@ module BetterErrors
|
|
15
15
|
|
16
16
|
it "does line continuation" do
|
17
17
|
output, prompt, filled = repl.send_input ""
|
18
|
-
output.
|
19
|
-
prompt.
|
20
|
-
filled.
|
18
|
+
expect(output).to eq("=> nil\n")
|
19
|
+
expect(prompt).to eq(">>")
|
20
|
+
expect(filled).to eq("")
|
21
21
|
|
22
22
|
output, prompt, filled = repl.send_input "def f(x)"
|
23
|
-
output.
|
24
|
-
prompt.
|
25
|
-
filled.
|
23
|
+
expect(output).to eq("")
|
24
|
+
expect(prompt).to eq("..")
|
25
|
+
expect(filled).to eq(" ")
|
26
26
|
|
27
27
|
output, prompt, filled = repl.send_input "end"
|
28
28
|
if RUBY_VERSION >= "2.1.0"
|
29
|
-
output.
|
29
|
+
expect(output).to eq("=> :f\n")
|
30
30
|
else
|
31
|
-
output.
|
31
|
+
expect(output).to eq("=> nil\n")
|
32
32
|
end
|
33
|
-
prompt.
|
34
|
-
filled.
|
33
|
+
expect(prompt).to eq(">>")
|
34
|
+
expect(filled).to eq("")
|
35
35
|
end
|
36
36
|
|
37
37
|
it_behaves_like "a REPL provider"
|
@@ -1,18 +1,18 @@
|
|
1
1
|
shared_examples_for "a REPL provider" do
|
2
2
|
it "evaluates ruby code in a given context" do
|
3
3
|
repl.send_input("local_a = 456")
|
4
|
-
fresh_binding.eval("local_a").
|
4
|
+
expect(fresh_binding.eval("local_a")).to eq(456)
|
5
5
|
end
|
6
6
|
|
7
7
|
it "returns a tuple of output and the new prompt" do
|
8
8
|
output, prompt = repl.send_input("1 + 2")
|
9
|
-
output.
|
10
|
-
prompt.
|
9
|
+
expect(output).to eq("=> 3\n")
|
10
|
+
expect(prompt).to eq(">>")
|
11
11
|
end
|
12
12
|
|
13
13
|
it "doesn't barf if the code throws an exception" do
|
14
14
|
output, prompt = repl.send_input("raise Exception")
|
15
|
-
output.
|
16
|
-
prompt.
|
15
|
+
expect(output).to include "Exception: Exception"
|
16
|
+
expect(prompt).to eq(">>")
|
17
17
|
end
|
18
18
|
end
|
@@ -4,80 +4,80 @@ module BetterErrors
|
|
4
4
|
describe StackFrame do
|
5
5
|
context "#application?" do
|
6
6
|
it "is true for application filenames" do
|
7
|
-
BetterErrors.
|
7
|
+
allow(BetterErrors).to receive(:application_root).and_return("/abc/xyz")
|
8
8
|
frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index")
|
9
9
|
|
10
|
-
frame.
|
10
|
+
expect(frame).to be_application
|
11
11
|
end
|
12
12
|
|
13
13
|
it "is false for everything else" do
|
14
|
-
BetterErrors.
|
14
|
+
allow(BetterErrors).to receive(:application_root).and_return("/abc/xyz")
|
15
15
|
frame = StackFrame.new("/abc/nope", 123, "foo")
|
16
16
|
|
17
|
-
frame.
|
17
|
+
expect(frame).not_to be_application
|
18
18
|
end
|
19
19
|
|
20
20
|
it "doesn't care if no application_root is set" do
|
21
21
|
frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index")
|
22
22
|
|
23
|
-
frame.
|
23
|
+
expect(frame).not_to be_application
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
27
|
context "#gem?" do
|
28
28
|
it "is true for gem filenames" do
|
29
|
-
Gem.
|
29
|
+
allow(Gem).to receive(:path).and_return(["/abc/xyz"])
|
30
30
|
frame = StackFrame.new("/abc/xyz/gems/whatever-1.2.3/lib/whatever.rb", 123, "foo")
|
31
31
|
|
32
|
-
frame.
|
32
|
+
expect(frame).to be_gem
|
33
33
|
end
|
34
34
|
|
35
35
|
it "is false for everything else" do
|
36
|
-
Gem.
|
36
|
+
allow(Gem).to receive(:path).and_return(["/abc/xyz"])
|
37
37
|
frame = StackFrame.new("/abc/nope", 123, "foo")
|
38
38
|
|
39
|
-
frame.
|
39
|
+
expect(frame).not_to be_gem
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
43
|
context "#application_path" do
|
44
44
|
it "chops off the application root" do
|
45
|
-
BetterErrors.
|
45
|
+
allow(BetterErrors).to receive(:application_root).and_return("/abc/xyz")
|
46
46
|
frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index")
|
47
47
|
|
48
|
-
frame.application_path.
|
48
|
+
expect(frame.application_path).to eq("app/controllers/crap_controller.rb")
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
52
|
context "#gem_path" do
|
53
53
|
it "chops of the gem path and stick (gem) there" do
|
54
|
-
Gem.
|
54
|
+
allow(Gem).to receive(:path).and_return(["/abc/xyz"])
|
55
55
|
frame = StackFrame.new("/abc/xyz/gems/whatever-1.2.3/lib/whatever.rb", 123, "foo")
|
56
56
|
|
57
|
-
frame.gem_path.
|
57
|
+
expect(frame.gem_path).to eq("whatever (1.2.3) lib/whatever.rb")
|
58
58
|
end
|
59
59
|
|
60
60
|
it "prioritizes gem path over application path" do
|
61
|
-
BetterErrors.
|
62
|
-
Gem.
|
61
|
+
allow(BetterErrors).to receive(:application_root).and_return("/abc/xyz")
|
62
|
+
allow(Gem).to receive(:path).and_return(["/abc/xyz/vendor"])
|
63
63
|
frame = StackFrame.new("/abc/xyz/vendor/gems/whatever-1.2.3/lib/whatever.rb", 123, "foo")
|
64
64
|
|
65
|
-
frame.gem_path.
|
65
|
+
expect(frame.gem_path).to eq("whatever (1.2.3) lib/whatever.rb")
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
69
|
context "#pretty_path" do
|
70
70
|
it "returns #application_path for application paths" do
|
71
|
-
BetterErrors.
|
71
|
+
allow(BetterErrors).to receive(:application_root).and_return("/abc/xyz")
|
72
72
|
frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index")
|
73
|
-
frame.pretty_path.
|
73
|
+
expect(frame.pretty_path).to eq(frame.application_path)
|
74
74
|
end
|
75
75
|
|
76
76
|
it "returns #gem_path for gem paths" do
|
77
|
-
Gem.
|
77
|
+
allow(Gem).to receive(:path).and_return(["/abc/xyz"])
|
78
78
|
frame = StackFrame.new("/abc/xyz/gems/whatever-1.2.3/lib/whatever.rb", 123, "foo")
|
79
79
|
|
80
|
-
frame.pretty_path.
|
80
|
+
expect(frame.pretty_path).to eq(frame.gem_path)
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
@@ -87,36 +87,36 @@ module BetterErrors
|
|
87
87
|
rescue SyntaxError => syntax_error
|
88
88
|
end
|
89
89
|
frames = StackFrame.from_exception(syntax_error)
|
90
|
-
frames.first.filename.
|
91
|
-
frames.first.line.
|
90
|
+
expect(frames.first.filename).to eq("my_file.rb")
|
91
|
+
expect(frames.first.line).to eq(123)
|
92
92
|
end
|
93
93
|
|
94
94
|
it "doesn't blow up if no method name is given" do
|
95
95
|
error = StandardError.allocate
|
96
96
|
|
97
|
-
error.
|
97
|
+
allow(error).to receive(:backtrace).and_return(["foo.rb:123"])
|
98
98
|
frames = StackFrame.from_exception(error)
|
99
|
-
frames.first.filename.
|
100
|
-
frames.first.line.
|
99
|
+
expect(frames.first.filename).to eq("foo.rb")
|
100
|
+
expect(frames.first.line).to eq(123)
|
101
101
|
|
102
|
-
error.
|
102
|
+
allow(error).to receive(:backtrace).and_return(["foo.rb:123: this is an error message"])
|
103
103
|
frames = StackFrame.from_exception(error)
|
104
|
-
frames.first.filename.
|
105
|
-
frames.first.line.
|
104
|
+
expect(frames.first.filename).to eq("foo.rb")
|
105
|
+
expect(frames.first.line).to eq(123)
|
106
106
|
end
|
107
107
|
|
108
108
|
it "ignores a backtrace line if its format doesn't make any sense at all" do
|
109
109
|
error = StandardError.allocate
|
110
|
-
error.
|
110
|
+
allow(error).to receive(:backtrace).and_return(["foo.rb:123:in `foo'", "C:in `find'", "bar.rb:123:in `bar'"])
|
111
111
|
frames = StackFrame.from_exception(error)
|
112
|
-
frames.count.
|
112
|
+
expect(frames.count).to eq(2)
|
113
113
|
end
|
114
114
|
|
115
115
|
it "doesn't blow up if a filename contains a colon" do
|
116
116
|
error = StandardError.allocate
|
117
|
-
error.
|
117
|
+
allow(error).to receive(:backtrace).and_return(["crap:filename.rb:123"])
|
118
118
|
frames = StackFrame.from_exception(error)
|
119
|
-
frames.first.filename.
|
119
|
+
expect(frames.first.filename).to eq("crap:filename.rb")
|
120
120
|
end
|
121
121
|
|
122
122
|
it "doesn't blow up with a BasicObject as frame binding" do
|
@@ -125,7 +125,7 @@ module BetterErrors
|
|
125
125
|
::Kernel.binding
|
126
126
|
end
|
127
127
|
frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index", obj.my_binding)
|
128
|
-
frame.class_name.
|
128
|
+
expect(frame.class_name).to eq('BasicObject')
|
129
129
|
end
|
130
130
|
|
131
131
|
it "sets method names properly" do
|
@@ -140,11 +140,11 @@ module BetterErrors
|
|
140
140
|
|
141
141
|
frame = StackFrame.from_exception(obj.my_method).first
|
142
142
|
if BetterErrors.binding_of_caller_available?
|
143
|
-
frame.method_name.
|
144
|
-
frame.class_name.
|
143
|
+
expect(frame.method_name).to eq("#my_method")
|
144
|
+
expect(frame.class_name).to eq("String")
|
145
145
|
else
|
146
|
-
frame.method_name.
|
147
|
-
frame.class_name.
|
146
|
+
expect(frame.method_name).to eq("my_method")
|
147
|
+
expect(frame.class_name).to eq(nil)
|
148
148
|
end
|
149
149
|
end
|
150
150
|
|
data/spec/better_errors_spec.rb
CHANGED
@@ -3,38 +3,38 @@ require "spec_helper"
|
|
3
3
|
describe BetterErrors do
|
4
4
|
context ".editor" do
|
5
5
|
it "defaults to textmate" do
|
6
|
-
subject.editor["foo.rb", 123].
|
6
|
+
expect(subject.editor["foo.rb", 123]).to eq("txmt://open?url=file://foo.rb&line=123")
|
7
7
|
end
|
8
8
|
|
9
9
|
it "url escapes the filename" do
|
10
|
-
subject.editor["&.rb", 0].
|
10
|
+
expect(subject.editor["&.rb", 0]).to eq("txmt://open?url=file://%26.rb&line=0")
|
11
11
|
end
|
12
12
|
|
13
13
|
[:emacs, :emacsclient].each do |editor|
|
14
14
|
it "uses emacs:// scheme when set to #{editor.inspect}" do
|
15
15
|
subject.editor = editor
|
16
|
-
subject.editor[].
|
16
|
+
expect(subject.editor[]).to start_with "emacs://"
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
20
|
[:macvim, :mvim].each do |editor|
|
21
21
|
it "uses mvim:// scheme when set to #{editor.inspect}" do
|
22
22
|
subject.editor = editor
|
23
|
-
subject.editor[].
|
23
|
+
expect(subject.editor[]).to start_with "mvim://"
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
27
|
[:sublime, :subl, :st].each do |editor|
|
28
28
|
it "uses subl:// scheme when set to #{editor.inspect}" do
|
29
29
|
subject.editor = editor
|
30
|
-
subject.editor[].
|
30
|
+
expect(subject.editor[]).to start_with "subl://"
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
34
|
[:textmate, :txmt, :tm].each do |editor|
|
35
35
|
it "uses txmt:// scheme when set to #{editor.inspect}" do
|
36
36
|
subject.editor = editor
|
37
|
-
subject.editor[].
|
37
|
+
expect(subject.editor[]).to start_with "txmt://"
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
@@ -42,7 +42,7 @@ describe BetterErrors do
|
|
42
42
|
it "uses emacs:// scheme when EDITOR=#{editor}" do
|
43
43
|
ENV["EDITOR"] = editor
|
44
44
|
subject.editor = subject.default_editor
|
45
|
-
subject.editor[].
|
45
|
+
expect(subject.editor[]).to start_with "emacs://"
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
@@ -50,15 +50,15 @@ describe BetterErrors do
|
|
50
50
|
it "uses mvim:// scheme when EDITOR=#{editor}" do
|
51
51
|
ENV["EDITOR"] = editor
|
52
52
|
subject.editor = subject.default_editor
|
53
|
-
subject.editor[].
|
53
|
+
expect(subject.editor[]).to start_with "mvim://"
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
57
|
["subl -w", "/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl"].each do |editor|
|
58
|
-
it "uses
|
58
|
+
it "uses subl:// scheme when EDITOR=#{editor}" do
|
59
59
|
ENV["EDITOR"] = editor
|
60
60
|
subject.editor = subject.default_editor
|
61
|
-
subject.editor[].
|
61
|
+
expect(subject.editor[]).to start_with "subl://"
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
@@ -66,7 +66,7 @@ describe BetterErrors do
|
|
66
66
|
it "uses txmt:// scheme when EDITOR=#{editor}" do
|
67
67
|
ENV["EDITOR"] = editor
|
68
68
|
subject.editor = subject.default_editor
|
69
|
-
subject.editor[].
|
69
|
+
expect(subject.editor[]).to start_with "txmt://"
|
70
70
|
end
|
71
71
|
end
|
72
72
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: better_errors
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Charlie Somerville
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-07-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: erubis
|
@@ -70,6 +70,15 @@ files:
|
|
70
70
|
- README.md
|
71
71
|
- Rakefile
|
72
72
|
- better_errors.gemspec
|
73
|
+
- feature-screenshots/1-application-error.jpg
|
74
|
+
- feature-screenshots/2-other-application-frame.jpg
|
75
|
+
- feature-screenshots/3-live-shell.jpg
|
76
|
+
- feature-screenshots/4-other-frames.jpg
|
77
|
+
- feature-screenshots/5-open-editor.jpg
|
78
|
+
- feature-screenshots/6-local-variables.jpg
|
79
|
+
- feature-screenshots/7-non-html-requests.jpg
|
80
|
+
- feature-screenshots/8-xhr-shows-text-error.jpg
|
81
|
+
- feature-screenshots/9-xhr-error-in-manual-console.jpg
|
73
82
|
- lib/better_errors.rb
|
74
83
|
- lib/better_errors/code_formatter.rb
|
75
84
|
- lib/better_errors/code_formatter/html.rb
|
@@ -119,7 +128,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
128
|
version: '0'
|
120
129
|
requirements: []
|
121
130
|
rubyforge_project:
|
122
|
-
rubygems_version: 2.
|
131
|
+
rubygems_version: 2.6.8
|
123
132
|
signing_key:
|
124
133
|
specification_version: 4
|
125
134
|
summary: Better error page for Rails and other Rack apps
|