Passing multiple error classes to ruby's rescue clause in a DRY fashion

44,358

Solution 1

You can use an array with the splat operator *.

EXCEPTIONS = [FooException, BarException]

begin
  a = rand
  if a > 0.5
    raise FooException
  else
    raise BarException
  end
rescue *EXCEPTIONS
  puts "rescued!"
end

If you are going to use a constant for the array as above (with EXCEPTIONS), note that you cannot define it within a definition, and also if you define it in some other class, you have to refer to it with its namespace. Actually, it does not have to be a constant.


Splat Operator

The splat operator * "unpacks" an array in its position so that

rescue *EXCEPTIONS

means the same as

rescue FooException, BarException

You can also use it within an array literal as

[BazException, *EXCEPTIONS, BangExcepion]

which is the same as

[BazException, FooException, BarException, BangExcepion]

or in an argument position

method(BazException, *EXCEPTIONS, BangExcepion)

which means

method(BazException, FooException, BarException, BangExcepion)

[] expands to vacuity:

[a, *[], b] # => [a, b]

One difference between ruby 1.8 and ruby 1.9 is with nil.

[a, *nil, b] # => [a, b]       (ruby 1.9)
[a, *nil, b] # => [a, nil, b]  (ruby 1.8)

Be careful with objects on which to_a is defined, as to_a will be applied in such cases:

[a, *{k: :v}, b] # => [a, [:k, :v], b]

With other types of objects, it returns itself.

[1, *2, 3] # => [1, 2, 3]

Solution 2

Edit / Update

I missed the whole point of the original question.
While the accepted answer is a valid one, in my opinion it's not a good practice to use the suggested technique. One can always have a wrapping function with the desired (and generic) try/rescue.


While the answer given by @sawa is technically right, I think it misuses Ruby's exception handling mechanism.

As the comment by Peter Ehrlich suggests (by pointing to an old blog post by Mike Ferrier), Ruby is already equipped with a DRY exception handler mechanism:

puts 'starting up'
begin
  case rand(3)
  when 0
    ([] + '')
  when 1
    (foo)
  when 2
    (3 / 0)
  end
rescue TypeError, NameError => e
  puts "oops: #{e.message}"
rescue Exception => e
  puts "ouch, #{e}"
end
puts 'done'

By using this technique, we can access the exception object, which usually has some valuable information in it.

Solution 3

I just ran into this issue and found an alternate solution. In the case your FooException and BarException are all going to be custom exception classes and particularly if they are all thematically related, you can structure your inheritance hierarchy such that they will all inherit from the same parent class and then rescue only the parent class.

For example I had three exceptions: FileNamesMissingError,InputFileMissingError, and OutputDirectoryError that I wanted to rescue with one statement. I made another exception class called FileLoadError and then set up the above three exceptions to inherit from it. I then rescued only FileLoadError.

Like this:

class FileLoadError < StandardError
end

class FileNamesMissingError < FileLoadError
end

class InputFileMissingError < FileLoadError
end

class OutputDirectoryError < FileLoadError
end

[FileNamesMissingError,
 InputFileMissingError,
 OutputDirectoryError].each do |error| 
   begin  
     raise error
   rescue FileLoadError => e
     puts "Rescuing #{e.class}."
   end 
end
Share:
44,358

Related videos on Youtube

apb
Author by

apb

Bug fixer

Updated on July 08, 2022

Comments

  • apb
    apb almost 2 years

    I have some code that needs to rescue multiple types of exceptions in ruby:

    begin
      a = rand
      if a > 0.5
        raise FooException
      else
        raise BarException
      end
    rescue FooException, BarException
      puts "rescued!"
    end
    

    What I'd like to do is somehow store the list of exception types that I want to rescue somewhere and pass those types to the rescue clause:

    EXCEPTIONS = [FooException, BarException]
    

    and then:

    rescue EXCEPTIONS
    

    Is this even possible, and is it possible without some really hack-y calls to eval? I'm not hopeful given that I'm seeing TypeError: class or module required for rescue clause when I attempt the above.

    • Roman
      Roman about 13 years
      What about rescue *EXCEPTIONS ?
  • apb
    apb about 13 years
    This appears to work even in ruby 1.8.7. What is the term for using the '*' character in front of EXCEPTIONS in this case? Would like to learn a bit more.
  • sawa
    sawa about 13 years
    @Andy It's called splat. It usually has the effect of decomposing an array into comma separated objects. When used in the argument receiving position of a method definition, it does the other way: put the arguments together into an array. It's quite useful in various occasions. Good to know that it works with 1.8.7. I edited my answer accordingly.
  • Peter Ehrlich
    Peter Ehrlich over 11 years
    Note that if you want to access the exception instance, use this syntax: rescue InvalidRequestError, CardError => e (see mikeferrier.com/2012/05/19/…)
  • William S
    William S about 4 years
    This syntax works just fine: rescue *EXCEPTIONS => e, where EXCEPTIONS is an array of exception class names.
  • Segfault
    Segfault about 3 years
    -1 This is the syntax OP has in his question. He wants to extract that list into a place where it is easier to maintain, I assume because the same list of exception types has to be repeated in multiple places and updates to the list have been error prone.
  • Ron Klein
    Ron Klein about 3 years
    @Segfault only after reading your comment I finally understood OP's question..