better_errors 2.2.0 → 2.3.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/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
|