better_errors 2.1.1 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
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
|