Skip to content

Commit 32fc91d

Browse files
Show real cause of ActionView::Template::Error
1 parent 3939536 commit 32fc91d

File tree

2 files changed

+48
-37
lines changed

2 files changed

+48
-37
lines changed

Diff for: lib/better_errors/raised_exception.rb

+12-6
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,18 @@ class RaisedException
44
attr_reader :exception, :message, :backtrace
55

66
def initialize(exception)
7-
if exception.respond_to?(:original_exception) && exception.original_exception
8-
# This supports some specific Rails exceptions, and is not intended to act the same as `#cause`.
7+
if exception.class.name == "ActionView::Template::Error" && exception.respond_to?(:cause)
8+
# Rails 6+ exceptions of this type wrap the "real" exception, and the real exception
9+
# is actually more useful than the ActionView-provided wrapper. Once Better Errors
10+
# supports showing all exceptions in the cause stack, this should go away. Or perhaps
11+
# this can be changed to provide guidance by showing the second error in the cause stack
12+
# under this condition.
13+
exception = exception.cause if exception.cause
14+
elsif exception.respond_to?(:original_exception) && exception.original_exception
15+
# This supports some specific Rails exceptions, and this is not intended to act the same as
16+
# the Ruby's {Exception#cause}.
17+
# It's possible this should only support ActionView::Template::Error, but by not changing
18+
# this we're preserving longstanding behavior of Better Errors with Rails < 6.
919
exception = exception.original_exception
1020
end
1121

@@ -57,10 +67,6 @@ def setup_backtrace_from_backtrace
5767

5868
def massage_syntax_error
5969
case exception.class.to_s
60-
when "ActionView::Template::Error"
61-
if exception.respond_to?(:file_name) && exception.respond_to?(:line_number)
62-
backtrace.unshift(StackFrame.new(exception.file_name, exception.line_number.to_i, "view template"))
63-
end
6470
when "Haml::SyntaxError", "Sprockets::Coffeelint::Error"
6571
if /\A(.+?):(\d+)/ =~ exception.backtrace.first
6672
backtrace.unshift(StackFrame.new($1, $2.to_i, ""))

Diff for: spec/better_errors/raised_exception_spec.rb

+36-31
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,32 @@ module BetterErrors
1010
its(:message) { is_expected.to eq "whoops" }
1111
its(:type) { is_expected.to eq RuntimeError }
1212

13-
context "when the exception wraps another exception" do
13+
context 'when the exception is an ActionView::Template::Error that responds to #cause (Rails 6+)' do
14+
before do
15+
stub_const(
16+
"ActionView::Template::Error",
17+
Class.new(StandardError) do
18+
def cause
19+
RuntimeError.new("something went wrong!")
20+
end
21+
end
22+
)
23+
end
24+
let(:exception) {
25+
ActionView::Template::Error.new("undefined method `something!' for #<Class:0x00deadbeef>")
26+
}
27+
28+
its(:message) { is_expected.to eq "something went wrong!" }
29+
its(:type) { is_expected.to eq RuntimeError }
30+
end
31+
32+
context 'when the exception is a Rails < 6 exception that has an #original_exception' do
1433
let(:original_exception) { RuntimeError.new("something went wrong!") }
1534
let(:exception) { double(:original_exception => original_exception) }
1635

1736
its(:exception) { is_expected.to eq original_exception }
18-
its(:message) { is_expected.to eq "something went wrong!" }
37+
its(:message) { is_expected.to eq "something went wrong!" }
38+
its(:type) { is_expected.to eq RuntimeError }
1939
end
2040

2141
context "when the exception is a SyntaxError" do
@@ -50,35 +70,20 @@ module BetterErrors
5070
end
5171
end
5272

53-
context "when the exception is an ActionView::Template::Error" do
54-
before do
55-
stub_const(
56-
"ActionView::Template::Error",
57-
Class.new(StandardError) do
58-
def file_name
59-
"app/views/foo/bar.haml"
60-
end
61-
62-
def line_number
63-
42
64-
end
65-
end
66-
)
67-
end
68-
69-
let(:exception) {
70-
ActionView::Template::Error.new("undefined method `something!' for #<Class:0x00deadbeef>")
71-
}
72-
73-
its(:message) { is_expected.to eq "undefined method `something!' for #<Class:0x00deadbeef>" }
74-
its(:type) { is_expected.to eq ActionView::Template::Error }
75-
76-
it "has the right filename and line number in the backtrace" do
77-
expect(subject.backtrace.first.filename).to eq("app/views/foo/bar.haml")
78-
expect(subject.backtrace.first.line).to eq(42)
79-
end
80-
end
81-
73+
# context "when the exception is an ActionView::Template::Error" do
74+
#
75+
# let(:exception) {
76+
# ActionView::Template::Error.new("undefined method `something!' for #<Class:0x00deadbeef>")
77+
# }
78+
#
79+
# its(:message) { is_expected.to eq "undefined method `something!' for #<Class:0x00deadbeef>" }
80+
#
81+
# it "has the right filename and line number in the backtrace" do
82+
# expect(subject.backtrace.first.filename).to eq("app/views/foo/bar.haml")
83+
# expect(subject.backtrace.first.line).to eq(42)
84+
# end
85+
# end
86+
#
8287
context "when the exception is a Coffeelint syntax error" do
8388
before do
8489
stub_const("Sprockets::Coffeelint::Error", Class.new(SyntaxError))

0 commit comments

Comments
 (0)