Search code examples
rubyexceptionrescue

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


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.


Solution

  • 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]