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.

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