Home > Software engineering >  How do I prevent two exception stacks when catching and re-raising Ruby exceptions?
How do I prevent two exception stacks when catching and re-raising Ruby exceptions?

Time:08-29

In a larger project, I am catching specific exceptions, adding some details to the message and then re-raising the same exception. Here is the basic idea of what I'm doing.

#!/usr/bin/env ruby

begin
  puts 12 / 0
rescue ZeroDivisionError => e
  raise e.exception("new message #{e.message}")
end

When I execute temp.rb from the command line, two exceptions are printed:

 % ./temp.rb
./temp.rb:4:in `/': new message divided by 0 (ZeroDivisionError)
    from ./temp.rb:4:in `<main>'
./temp.rb:4:in `/': divided by 0 (ZeroDivisionError)
    from ./temp.rb:4:in `<main>'

I'm assuming that there is a stack or a list of exceptions somewhere and the exit process of Ruby prints out the entire list or stack of exceptions. I can see great benefits to this. But in my case, I'd like to get rid of the original exception from the list or stack. Is there a way to do this?

CodePudding user response:

The "second" (original) exception is the cause of the rescued exception which is references by your new exception as it is created with Exception#exception.

Starting with Ruby 3.1, irb prints the details of the cause in addition to the actual exception to aid in debugging. Previous Ruby versions have ignored the cause here.

Thus, you can use other means to handle your exceptions rather than using Ruby's default handler of printing the details of an otherwise unhandled exception, e.g. by adding an explicit exception handler. Alternatively, you can also explicitly set the cause as you are creating your new exception:

begin
  puts 12 / 0
rescue ZeroDivisionError => e
  raise e.class, "new message #{e.message}", cause: nil
end

CodePudding user response:

May be something like this to extend Exception with custom method

Exception.class_eval do
  def add_message(msg)
    mod =
      Module.new do
        define_method :to_s do
          "#{super()} #{msg}"
        end
      end

    extend mod
  end
end

And then

begin
  puts 12 / 0
rescue ZeroDivisionError => e
  raise e.add_message("(even don't try it)")
end
  • Related