better_errors 2.2.0 → 2.3.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/README.md +19 -51
- data/better_errors.gemspec +5 -3
- data/lib/better_errors.rb +1 -1
- data/lib/better_errors/error_page.rb +16 -9
- data/lib/better_errors/middleware.rb +30 -3
- data/lib/better_errors/repl.rb +3 -1
- data/lib/better_errors/repl/basic.rb +1 -1
- data/lib/better_errors/repl/pry.rb +11 -1
- data/lib/better_errors/stack_frame.rb +7 -0
- data/lib/better_errors/templates/main.erb +7 -4
- data/lib/better_errors/version.rb +1 -1
- metadata +6 -40
- data/Rakefile +0 -13
- 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/spec/better_errors/code_formatter_spec.rb +0 -92
- data/spec/better_errors/error_page_spec.rb +0 -92
- data/spec/better_errors/middleware_spec.rb +0 -188
- data/spec/better_errors/raised_exception_spec.rb +0 -73
- data/spec/better_errors/repl/basic_spec.rb +0 -18
- data/spec/better_errors/repl/pry_spec.rb +0 -40
- data/spec/better_errors/repl/shared_examples.rb +0 -18
- data/spec/better_errors/stack_frame_spec.rb +0 -157
- data/spec/better_errors/support/my_source.rb +0 -20
- data/spec/better_errors_spec.rb +0 -73
- data/spec/spec_helper.rb +0 -5
- data/spec/without_binding_of_caller.rb +0 -9
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -1,92 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
module BetterErrors
|
4
|
-
describe CodeFormatter do
|
5
|
-
let(:filename) { File.expand_path("../support/my_source.rb", __FILE__) }
|
6
|
-
|
7
|
-
let(:formatter) { CodeFormatter.new(filename, 8) }
|
8
|
-
|
9
|
-
it "picks an appropriate scanner" do
|
10
|
-
expect(formatter.coderay_scanner).to eq(:ruby)
|
11
|
-
end
|
12
|
-
|
13
|
-
it "shows 5 lines of context" do
|
14
|
-
expect(formatter.line_range).to eq(3..13)
|
15
|
-
|
16
|
-
expect(formatter.context_lines).to eq([
|
17
|
-
"three\n",
|
18
|
-
"four\n",
|
19
|
-
"five\n",
|
20
|
-
"six\n",
|
21
|
-
"seven\n",
|
22
|
-
"eight\n",
|
23
|
-
"nine\n",
|
24
|
-
"ten\n",
|
25
|
-
"eleven\n",
|
26
|
-
"twelve\n",
|
27
|
-
"thirteen\n"
|
28
|
-
])
|
29
|
-
end
|
30
|
-
|
31
|
-
it "works when the line is right on the edge" do
|
32
|
-
formatter = CodeFormatter.new(filename, 20)
|
33
|
-
expect(formatter.line_range).to eq(15..20)
|
34
|
-
end
|
35
|
-
|
36
|
-
describe CodeFormatter::HTML do
|
37
|
-
it "highlights the erroring line" do
|
38
|
-
formatter = CodeFormatter::HTML.new(filename, 8)
|
39
|
-
expect(formatter.output).to match(/highlight.*eight/)
|
40
|
-
end
|
41
|
-
|
42
|
-
it "works when the line is right on the edge" do
|
43
|
-
formatter = CodeFormatter::HTML.new(filename, 20)
|
44
|
-
expect(formatter.output).not_to eq(formatter.source_unavailable)
|
45
|
-
end
|
46
|
-
|
47
|
-
it "doesn't barf when the lines don't make any sense" do
|
48
|
-
formatter = CodeFormatter::HTML.new(filename, 999)
|
49
|
-
expect(formatter.output).to eq(formatter.source_unavailable)
|
50
|
-
end
|
51
|
-
|
52
|
-
it "doesn't barf when the file doesn't exist" do
|
53
|
-
formatter = CodeFormatter::HTML.new("fkdguhskd7e l", 1)
|
54
|
-
expect(formatter.output).to eq(formatter.source_unavailable)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
describe CodeFormatter::Text do
|
59
|
-
it "highlights the erroring line" do
|
60
|
-
formatter = CodeFormatter::Text.new(filename, 8)
|
61
|
-
expect(formatter.output).to eq <<-TEXT.gsub(/^ /, "")
|
62
|
-
3 three
|
63
|
-
4 four
|
64
|
-
5 five
|
65
|
-
6 six
|
66
|
-
7 seven
|
67
|
-
> 8 eight
|
68
|
-
9 nine
|
69
|
-
10 ten
|
70
|
-
11 eleven
|
71
|
-
12 twelve
|
72
|
-
13 thirteen
|
73
|
-
TEXT
|
74
|
-
end
|
75
|
-
|
76
|
-
it "works when the line is right on the edge" do
|
77
|
-
formatter = CodeFormatter::Text.new(filename, 20)
|
78
|
-
expect(formatter.output).not_to eq(formatter.source_unavailable)
|
79
|
-
end
|
80
|
-
|
81
|
-
it "doesn't barf when the lines don't make any sense" do
|
82
|
-
formatter = CodeFormatter::Text.new(filename, 999)
|
83
|
-
expect(formatter.output).to eq(formatter.source_unavailable)
|
84
|
-
end
|
85
|
-
|
86
|
-
it "doesn't barf when the file doesn't exist" do
|
87
|
-
formatter = CodeFormatter::Text.new("fkdguhskd7e l", 1)
|
88
|
-
expect(formatter.output).to eq(formatter.source_unavailable)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
@@ -1,92 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
module BetterErrors
|
4
|
-
describe ErrorPage do
|
5
|
-
let!(:exception) { raise ZeroDivisionError, "you divided by zero you silly goose!" rescue $! }
|
6
|
-
|
7
|
-
let(:error_page) { ErrorPage.new exception, { "PATH_INFO" => "/some/path" } }
|
8
|
-
|
9
|
-
let(:response) { error_page.render }
|
10
|
-
|
11
|
-
let(:empty_binding) {
|
12
|
-
local_a = :value_for_local_a
|
13
|
-
local_b = :value_for_local_b
|
14
|
-
|
15
|
-
@inst_c = :value_for_inst_c
|
16
|
-
@inst_d = :value_for_inst_d
|
17
|
-
|
18
|
-
binding
|
19
|
-
}
|
20
|
-
|
21
|
-
it "includes the error message" do
|
22
|
-
expect(response).to include("you divided by zero you silly goose!")
|
23
|
-
end
|
24
|
-
|
25
|
-
it "includes the request path" do
|
26
|
-
expect(response).to include("/some/path")
|
27
|
-
end
|
28
|
-
|
29
|
-
it "includes the exception class" do
|
30
|
-
expect(response).to include("ZeroDivisionError")
|
31
|
-
end
|
32
|
-
|
33
|
-
context "variable inspection" do
|
34
|
-
let(:exception) { empty_binding.eval("raise") rescue $! }
|
35
|
-
|
36
|
-
if BetterErrors.binding_of_caller_available?
|
37
|
-
it "shows local variables" do
|
38
|
-
html = error_page.do_variables("index" => 0)[: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
|
-
end
|
44
|
-
else
|
45
|
-
it "tells the user to add binding_of_caller to their gemfile to get fancy features" do
|
46
|
-
html = error_page.do_variables("index" => 0)[:html]
|
47
|
-
expect(html).to include(%{gem "binding_of_caller"})
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
it "shows instance variables" do
|
52
|
-
html = error_page.do_variables("index" => 0)[: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
|
-
end
|
58
|
-
|
59
|
-
it "shows filter instance variables" do
|
60
|
-
allow(BetterErrors).to receive(:ignored_instance_variables).and_return([ :@inst_d ])
|
61
|
-
html = error_page.do_variables("index" => 0)[: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
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
it "doesn't die if the source file is not a real filename" do
|
70
|
-
allow(exception).to receive(:backtrace).and_return([
|
71
|
-
"<internal:prelude>:10:in `spawn_rack_application'"
|
72
|
-
])
|
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
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
@@ -1,188 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
module BetterErrors
|
4
|
-
describe Middleware do
|
5
|
-
let(:app) { Middleware.new(->env { ":)" }) }
|
6
|
-
let(:exception) { RuntimeError.new("oh no :(") }
|
7
|
-
|
8
|
-
it "passes non-error responses through" do
|
9
|
-
expect(app.call({})).to eq(":)")
|
10
|
-
end
|
11
|
-
|
12
|
-
it "calls the internal methods" do
|
13
|
-
expect(app).to receive :internal_call
|
14
|
-
app.call("PATH_INFO" => "/__better_errors/1/preform_awesomness")
|
15
|
-
end
|
16
|
-
|
17
|
-
it "calls the internal methods on any subfolder path" do
|
18
|
-
expect(app).to receive :internal_call
|
19
|
-
app.call("PATH_INFO" => "/any_sub/folder/path/__better_errors/1/preform_awesomness")
|
20
|
-
end
|
21
|
-
|
22
|
-
it "shows the error page" do
|
23
|
-
expect(app).to receive :show_error_page
|
24
|
-
app.call("PATH_INFO" => "/__better_errors/")
|
25
|
-
end
|
26
|
-
|
27
|
-
it "shows the error page on any subfolder path" do
|
28
|
-
expect(app).to receive :show_error_page
|
29
|
-
app.call("PATH_INFO" => "/any_sub/folder/path/__better_errors/")
|
30
|
-
end
|
31
|
-
|
32
|
-
it "doesn't show the error page to a non-local address" do
|
33
|
-
expect(app).not_to receive :better_errors_call
|
34
|
-
app.call("REMOTE_ADDR" => "1.2.3.4")
|
35
|
-
end
|
36
|
-
|
37
|
-
it "shows to a whitelisted IP" do
|
38
|
-
BetterErrors::Middleware.allow_ip! '77.55.33.11'
|
39
|
-
expect(app).to receive :better_errors_call
|
40
|
-
app.call("REMOTE_ADDR" => "77.55.33.11")
|
41
|
-
end
|
42
|
-
|
43
|
-
it "respects the X-Forwarded-For header" do
|
44
|
-
expect(app).not_to receive :better_errors_call
|
45
|
-
app.call(
|
46
|
-
"REMOTE_ADDR" => "127.0.0.1",
|
47
|
-
"HTTP_X_FORWARDED_FOR" => "1.2.3.4",
|
48
|
-
)
|
49
|
-
end
|
50
|
-
|
51
|
-
it "doesn't blow up when given a blank REMOTE_ADDR" do
|
52
|
-
expect { app.call("REMOTE_ADDR" => " ") }.to_not raise_error
|
53
|
-
end
|
54
|
-
|
55
|
-
it "doesn't blow up when given an IP address with a zone index" do
|
56
|
-
expect { app.call("REMOTE_ADDR" => "0:0:0:0:0:0:0:1%0" ) }.to_not raise_error
|
57
|
-
end
|
58
|
-
|
59
|
-
context "when requesting the /__better_errors manually" do
|
60
|
-
let(:app) { Middleware.new(->env { ":)" }) }
|
61
|
-
|
62
|
-
it "shows that no errors have been recorded" do
|
63
|
-
status, headers, body = app.call("PATH_INFO" => "/__better_errors")
|
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")
|
73
|
-
end
|
74
|
-
|
75
|
-
it "shows that no errors have been recorded on any subfolder path" do
|
76
|
-
status, headers, body = app.call("PATH_INFO" => "/any_sub/folder/path/__better_errors")
|
77
|
-
expect(body.join).to match /No errors have been recorded yet./
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
context "when handling an error" do
|
82
|
-
let(:app) { Middleware.new(->env { raise exception }) }
|
83
|
-
|
84
|
-
it "returns status 500" do
|
85
|
-
status, headers, body = app.call({})
|
86
|
-
|
87
|
-
expect(status).to eq(500)
|
88
|
-
end
|
89
|
-
|
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
|
97
|
-
|
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/)
|
112
|
-
end
|
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
|
124
|
-
|
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
|
-
})
|
129
|
-
|
130
|
-
status, _, body = app.call({})
|
131
|
-
|
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
|
136
|
-
|
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
|
-
})
|
141
|
-
|
142
|
-
status, _, body = app.call({})
|
143
|
-
|
144
|
-
expect(status).to eq(500)
|
145
|
-
expect(body.join).to match(/Other Exception/)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
it "returns ExceptionWrapper's status_code" do
|
151
|
-
ad_ew = double("ActionDispatch::ExceptionWrapper")
|
152
|
-
allow(ad_ew).to receive('new').with({}, exception) { double("ExceptionWrapper", status_code: 404) }
|
153
|
-
stub_const('ActionDispatch::ExceptionWrapper', ad_ew)
|
154
|
-
|
155
|
-
status, headers, body = app.call({})
|
156
|
-
|
157
|
-
expect(status).to eq(404)
|
158
|
-
end
|
159
|
-
|
160
|
-
it "returns UTF-8 error pages" do
|
161
|
-
status, headers, body = app.call({})
|
162
|
-
|
163
|
-
expect(headers["Content-Type"]).to match /charset=utf-8/
|
164
|
-
end
|
165
|
-
|
166
|
-
it "returns text pages by default" do
|
167
|
-
status, headers, body = app.call({})
|
168
|
-
|
169
|
-
expect(headers["Content-Type"]).to match /text\/plain/
|
170
|
-
end
|
171
|
-
|
172
|
-
it "returns HTML pages by default" do
|
173
|
-
# Chrome's 'Accept' header looks similar this.
|
174
|
-
status, headers, body = app.call("HTTP_ACCEPT" => "text/html,application/xhtml+xml;q=0.9,*/*")
|
175
|
-
|
176
|
-
expect(headers["Content-Type"]).to match /text\/html/
|
177
|
-
end
|
178
|
-
|
179
|
-
it "logs the exception" do
|
180
|
-
logger = Object.new
|
181
|
-
expect(logger).to receive :fatal
|
182
|
-
allow(BetterErrors).to receive(:logger).and_return(logger)
|
183
|
-
|
184
|
-
app.call({})
|
185
|
-
end
|
186
|
-
end
|
187
|
-
end
|
188
|
-
end
|
@@ -1,73 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
require "rspec/its"
|
3
|
-
|
4
|
-
module BetterErrors
|
5
|
-
describe RaisedException do
|
6
|
-
let(:exception) { RuntimeError.new("whoops") }
|
7
|
-
subject { RaisedException.new(exception) }
|
8
|
-
|
9
|
-
its(:exception) { should == exception }
|
10
|
-
its(:message) { should == "whoops" }
|
11
|
-
its(:type) { should == RuntimeError }
|
12
|
-
|
13
|
-
context "when the exception wraps another exception" do
|
14
|
-
let(:original_exception) { RuntimeError.new("something went wrong!") }
|
15
|
-
let(:exception) { double(:original_exception => original_exception) }
|
16
|
-
|
17
|
-
its(:exception) { should == original_exception }
|
18
|
-
its(:message) { should == "something went wrong!" }
|
19
|
-
end
|
20
|
-
|
21
|
-
context "when the exception is a syntax error" do
|
22
|
-
let(:exception) { SyntaxError.new("foo.rb:123: you made a typo!") }
|
23
|
-
|
24
|
-
its(:message) { should == "you made a typo!" }
|
25
|
-
its(:type) { should == SyntaxError }
|
26
|
-
|
27
|
-
it "has the right filename and line number in the backtrace" do
|
28
|
-
expect(subject.backtrace.first.filename).to eq("foo.rb")
|
29
|
-
expect(subject.backtrace.first.line).to eq(123)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
context "when the exception is a HAML syntax error" do
|
34
|
-
before do
|
35
|
-
stub_const("Haml::SyntaxError", Class.new(SyntaxError))
|
36
|
-
end
|
37
|
-
|
38
|
-
let(:exception) {
|
39
|
-
Haml::SyntaxError.new("you made a typo!").tap do |ex|
|
40
|
-
ex.set_backtrace(["foo.rb:123", "haml/internals/blah.rb:123456"])
|
41
|
-
end
|
42
|
-
}
|
43
|
-
|
44
|
-
its(:message) { should == "you made a typo!" }
|
45
|
-
its(:type) { should == Haml::SyntaxError }
|
46
|
-
|
47
|
-
it "has the right filename and line number in the backtrace" do
|
48
|
-
expect(subject.backtrace.first.filename).to eq("foo.rb")
|
49
|
-
expect(subject.backtrace.first.line).to eq(123)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
context "when the exception is a Coffeelint syntax error" do
|
54
|
-
before do
|
55
|
-
stub_const("Sprockets::Coffeelint::Error", Class.new(SyntaxError))
|
56
|
-
end
|
57
|
-
|
58
|
-
let(:exception) {
|
59
|
-
Sprockets::Coffeelint::Error.new("[stdin]:11:88: error: unexpected=").tap do |ex|
|
60
|
-
ex.set_backtrace(["app/assets/javascripts/files/index.coffee:11", "sprockets/coffeelint.rb:3"])
|
61
|
-
end
|
62
|
-
}
|
63
|
-
|
64
|
-
its(:message) { should == "[stdin]:11:88: error: unexpected=" }
|
65
|
-
its(:type) { should == Sprockets::Coffeelint::Error }
|
66
|
-
|
67
|
-
it "has the right filename and line number in the backtrace" do
|
68
|
-
expect(subject.backtrace.first.filename).to eq("app/assets/javascripts/files/index.coffee")
|
69
|
-
expect(subject.backtrace.first.line).to eq(11)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|