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.

Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +19 -51
  3. data/better_errors.gemspec +5 -3
  4. data/lib/better_errors.rb +1 -1
  5. data/lib/better_errors/error_page.rb +16 -9
  6. data/lib/better_errors/middleware.rb +30 -3
  7. data/lib/better_errors/repl.rb +3 -1
  8. data/lib/better_errors/repl/basic.rb +1 -1
  9. data/lib/better_errors/repl/pry.rb +11 -1
  10. data/lib/better_errors/stack_frame.rb +7 -0
  11. data/lib/better_errors/templates/main.erb +7 -4
  12. data/lib/better_errors/version.rb +1 -1
  13. metadata +6 -40
  14. data/Rakefile +0 -13
  15. data/feature-screenshots/1-application-error.jpg +0 -0
  16. data/feature-screenshots/2-other-application-frame.jpg +0 -0
  17. data/feature-screenshots/3-live-shell.jpg +0 -0
  18. data/feature-screenshots/4-other-frames.jpg +0 -0
  19. data/feature-screenshots/5-open-editor.jpg +0 -0
  20. data/feature-screenshots/6-local-variables.jpg +0 -0
  21. data/feature-screenshots/7-non-html-requests.jpg +0 -0
  22. data/feature-screenshots/8-xhr-shows-text-error.jpg +0 -0
  23. data/feature-screenshots/9-xhr-error-in-manual-console.jpg +0 -0
  24. data/spec/better_errors/code_formatter_spec.rb +0 -92
  25. data/spec/better_errors/error_page_spec.rb +0 -92
  26. data/spec/better_errors/middleware_spec.rb +0 -188
  27. data/spec/better_errors/raised_exception_spec.rb +0 -73
  28. data/spec/better_errors/repl/basic_spec.rb +0 -18
  29. data/spec/better_errors/repl/pry_spec.rb +0 -40
  30. data/spec/better_errors/repl/shared_examples.rb +0 -18
  31. data/spec/better_errors/stack_frame_spec.rb +0 -157
  32. data/spec/better_errors/support/my_source.rb +0 -20
  33. data/spec/better_errors_spec.rb +0 -73
  34. data/spec/spec_helper.rb +0 -5
  35. data/spec/without_binding_of_caller.rb +0 -9
@@ -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