 
                    I recently come across some surprising code involving exceptions which prompted me to look a bit deeper into exceptions in Ruby. In this post, I would like to share some of what I found.
Exception vs StandardError
This is where it all started. I had a script that did something like the following (it was not quite this simple, but it shows the important parts):
ruby
class MyLib
  class MyLibBaseError < Exception ; end
  class MyIOError < MyLibBaseError ; end
  def do_something
    raise MyIOError
  rescue
    puts "logging error: #{$!.message}" # $! is the last exception raised
  end
end
Calling do_something results in this:
ruby
> MyLib.new.do_something
MyLib::MyIOError: MyLib::MyIOError
	from (irb):70:in `do_something'
	from (irb):76
	from /Users/yuji/.rvm/rubies/ruby-2.1.7/bin/irb:11:in `<main>'
This was surprising to me: MyIOError is clearly raised, but puts is not called. This is because the blank rescue only catches StandardError and it’s subclasses, hence, any exception that is not a subclass of StandardError will not be caught by it.
The built-in exceptions are listed in the Ruby documentation for the Exception class.
Possible options for making this catch MyIOError include:
* Making MyLibBaseError inherit from StandardError.
* Modifying the rescue statement to be more specific about which exception classes to rescue from.
I did both of these, since I wanted MyIOError to be caught in any blank rescue statements around the app. And I also feel that using a blank rescue is not good practice, since I do not want to accidentally catch unintended exceptions.
Begin-rescue-end and def-rescue-end
Typically, rescue is used in the form of begin-rescue-end:
ruby
begin
  do_something
rescue => e
  do_something_with_error(e)
end
In addition to this, Ruby also has def-rescue-end in method definitions, for when you want to rescue from the entirety of a method:
ruby
def do_rescue
  do_something
rescue => e
  do_something_with_error(e)
end
This is a nicer equivalent to:
ruby
def do_rescue
  begin
    do_something
  rescue => e
    do_something_with_error(e)
  end
end
Default exception and optional arguments
By default, raise raises RuntimeError. If a string is passed to raise, it will raise a RuntimeError with the given string as its message.
ruby
> raise "message!"
RuntimeError: message!
In addition, optional arguments can be passed to raise:
* With 2 arguments, they will be the exception class to raise and the message.
* When 3 arguments are given, the first argument is the exception class, the second is the message, and the last one is an array of callback information.
ruby
> raise ZeroDivisionError, 'cannot divide by zero!', caller
ZeroDivisionError: cannot divide by zero!
	from /Users/yuji/.rvm/rubies/ruby-2.1.7/lib/ruby/2.1.0/irb/workspace.rb:86:in `eval'
	from /Users/yuji/.rvm/rubies/ruby-2.1.7/lib/ruby/2.1.0/irb/workspace.rb:86:in `evaluate'
  ...
Multiple rescues
Multiple rescue parts can be used to rescue different exceptions:
ruby
def multi_rescue
  1 / 0
rescue NameError
  puts "NameError raised."
rescue ZeroDivisionError
  puts "ZeroDivisionError raised."
end
If they are handled exactly the same, they can be rescued together:
ruby
def multi_rescue
  1 / 0
rescue NameError, ZeroDivisionError => e
  puts "#{e.class.name} raised. "
end
ruby
> multi_rescue
ZeroDivisionError raised.
 => nil
Ensure
Regardless of whether an exception is rescued, the code in the ensure section will be executed. This is useful, for example, to close a file that has been opened.
ruby
def read_file
  f = File.open('input_file.txt', 'r')
  # ... do something
rescue
  # error processing
  puts 'error handled'
ensure
  f.close
  puts 'closed file'
end
When running this with an error:
ruby
> read_file
error handled
closed file
  => nil
And without an error:
ruby
> read_file
closed file
  => nil
Else
This is not very common, but else can be used to execute code when nothing is raised. This is a little like else in Ruby’s case-when-else.
ruby
def use_else
  puts "doing something"
rescue NameError
  puts "NameError raised."
rescue ZeroDivisionError
  puts "ZeroDivisionError raised."
else
  puts "nothing raised"
ensure
  puts "ensuring."
end
ruby
> use_else
doing something
nothing raised
ensuring.
 => nil
## Raise and fail
In Ruby, raise and fail do the same. You can raise an exception or fail with an exception. raise seems to be more commonly used, but it is really up to you to decide which one expresses your intentions better.
ruby
> raise 'raise!'
RuntimeError: raise!
	from (irb):19
	from /Users/yuji/.rvm/rubies/ruby-2.1.7/bin/irb:11:in `<main>'
> fail 'fail!'
RuntimeError: fail!
	from (irb):20
	from /Users/yuji/.rvm/rubies/ruby-2.1.7/bin/irb:11:in `<main>'
One-liner
Rescue can be a one-liner:
ruby
> puts 1/0 rescue nil # ZeroDivisionError is suppressed here.
 => nil
This makes it really easy to ignore errors, though it is not possible to specify which exceptions to handle here. So it will catch StandardError and all its subclasses. I would not particularly recommend this, since I feel it is too easy to make mistakes by overusing this.
Ignoring exceptions
Rescuing and not doing anything with the caught exception is a common anti-pattern:
ruby
def ignore_error
   do_something
rescue
end
Things like this often cause problems in tracking down issues later on. I advise against this, and would suggest at least logging the error and re-raising the same exception, or, if you have to, adding a comment to the code so your colleagues know that the exception has intentionally been ignored.
Catch and throw
Although this is not a use of Exception, Ruby also has catch and throw constructs. This may be useful for people who are used to try-catch in other languages. It can also be used to jump out of loops.
ruby
def catch_foo
  catch :foo do
    puts "start"
    i = 0
    while i < 10
      j = 0
      while j < 10
        puts "i: #{i}, j: #{j}"
        throw :foo
        j += 1
      end
      i += 1
    end
    puts "end"
  end
  puts "all done!"
end
ruby
> catch_foo
start
i: 0, j: 0
all done!
 => nil
An uncaught throw will result in an ArgumentError:
ruby
> throw :foo
ArgumentError: uncaught throw :foo
Final Thoughts
Even though there are many different ways to write exception handling code, I would generally stick to basic, simple rules:
* Inherit from StandardError when defining your own exception classes.
* Specify which exceptions types to rescue.
* Use ensure to execute code regardless of whether or not there was an exception.
* Use many rescue sections for different errors, and else for when nothing is raised.
* Do not use a one-liner rescue.
* Do not simply ignore or suppress exceptions.
However, I myself may not always follow all of these rules; exception handling, by definition, is dealing with exceptional situations, and there may be situations where it makes sense not to follow the rules!