Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

For recent Ruby, show nested errors properly in backtraces #727

Open
wants to merge 1 commit into
base: master
from

Conversation

Projects
None yet
2 participants
@noahgibbs
Copy link

noahgibbs commented Dec 23, 2017

Recent Ruby versions store exceptions as Exception#cause when a different error is re-raised from a rescue block. This allows tracing a chain of exceptions to find out what inner exception is being "wrapped."

That's very useful! It's even more useful if Minitest actually displays the nested backtrace in a useful way. This pull request shows one way to make that happen.

@zenspider

This comment has been minimized.

Copy link
Member

zenspider commented Dec 25, 2017

@noahgibbs

This comment has been minimized.

Copy link
Author

noahgibbs commented Dec 26, 2017

If I do a quick test using Marshal, it looks like inner exceptions are Marshal-able. So I think you're good on that.

Specifically, in my local Ruby 2.3.1:

begin
  begin
    raise "Inner message"
  rescue
    raise "Outer message"
  end
rescue
    nest_e1 = $!
end

2.3.1 :007 > nest_e1
 => #<RuntimeError: Outer message>
2.3.1 :008 > nest_e1.cause
 => #<RuntimeError: Inner message>

nest_me1 = Marshal.dump nest_e1
nest_ne1 = Marshal.load(nest_me1)

2.3.1 :011 > nest_ne1.cause
 => #<RuntimeError: Inner message>
@zenspider

This comment has been minimized.

Copy link
Member

zenspider commented Dec 26, 2017

@noahgibbs

This comment has been minimized.

Copy link
Author

noahgibbs commented Dec 26, 2017

That's true. Your old approach (pre-nested-exceptions) had you replacing a non-Marshalable exception with a Marshalable one. You could replace the "error" object with one not created inside a rescue block to avoid the old one being included as "cause". My understanding is that you can't explicitly set or clear cause for an exception except by creating it in a rescue block or not - it automatically picks up dollar-bang as the value of "cause".

@zenspider

This comment has been minimized.

Copy link
Member

zenspider commented Jan 27, 2018

Mmmm... $! = nil before creating the new exception?

This is going to be a bitch to test...

@noahgibbs

This comment has been minimized.

Copy link
Author

noahgibbs commented Jan 27, 2018

There is a keyword to "raise" that will let you specify the inner exception, including as nil. That may be your best bet.

The raise keyword doesn't seem to be documented where I can find it, so I wrote it up: "http://engineering.appfolio.com/appfolio-engineering/2017/12/28/ruby-and-nested-exceptions" (scroll down to "Secrets.")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.