Thursday 13 July 2017

Ruby Exception Handling

What Are Exceptions?

Exceptions are Ruby’s way of dealing with unexpected events.

begin
1/0
rescue
puts "Got an exception, but I'm responding intelligently!"
end

# This program does not crash.
# Outputs: "Got an exception, but I'm responding intelligently!"


The exception still happens, but it doesn’t cause the program to crash because it was “rescued.” Instead of exiting, Ruby runs the code in the rescue block, which prints out a message. This is nice, but it has one big limitation. It tells us “something went wrong,” without letting us know what went wrong.

Exception Objects


All of the information about what went wrong is going to be contained in an exception object.

begin
1/0
rescue => e
puts "#{e.message}"
end

# Rescues all errors, an puts the exception object in `e`
rescue => e

# Rescues only ZeroDivisionError and puts the exception object in `e`
rescue ZeroDivisionError => e


begin
 # Any exceptions in here...
 1/0
rescue ZeroDivisionError => e
  puts "Exception Class: #{e.class.name}"
  puts"Exception Message:#{e.message}"
  put"Exception Backtrace:#{e.backtrace}"
end

# Outputs:
# Exception Class: ZeroDivisionError
# Exception Message: divided by 0
# Exception Backtrace: ...backtrace as an array...

Most exception objects will provide you with at least three pieces of data:
1. The type of exception, given by the class name.
2. An exception message
3. A backtrace

All of the information about what went wrong is going to be contained in an
exception object.


begin
 # Any exceptions in here...
 1/0
rescue ZeroDivisionError => e
  puts "Exception Class: #{e.class.name}"
  puts"Exception Message:#{e.message}"
  put"Exception Backtrace:#{e.backtrace}"
end

# Outputs:
# Exception Class: ZeroDivisionError
# Exception Message: divided by 0
# Exception Backtrace: ...backtrace as an array...


Most exception objects will provide you with at least three pieces of data:
1.The type of exception, given by the class name.
2.An exception message
3.A backtrace


Raising Your Own Exceptions

So far we’ve only talked about rescuing exceptions. You can also trigger your own exceptions. This process is called “raising” and you do it by calling the raise method.

begin
  # raises an ArgumentError with the message "you messed up!"
  raise ArgumentError.new("You messed up!")
rescue ArgumentError => e
  puts e.message
end

This being Ruby, raise can be called in several ways:

# This is my favorite because it's so explicit
  raise RuntimeError.new("You messed up!")

# ...produces the same result
  raise RuntimeError, "You messed up!"

# ...produces the same result. But you can only raise
# RuntimeErrors this way
  raise "You messed up!"

Making Custom Exceptions

Ruby’s built-in exceptions are great, but they don’t cover every possible use case.

To make a custom exception, just create a new class that inherits from StandardError.

class PermissionDeniedError < StandardError
end

raise PermissionDeniedError.new()


class PermissionDeniedError < StandardError
attr_reader :action
def initialize(message, action)
# Call the parent's constructor to set the message
super(message)
# Store the action in an instance variable
@action = action
end
end

# Then, when the user tries to delete something they don't
# have permission to delete, you might do something like this:
raise PermissionDeniedError.new("Permission Denied",:delete)

The  Class  Hierarchy


We just made a custom exception by subclassing StandardError, which itself subclasses Exception.

In fact,  if you look at  the  class hierarchy  of any  exception  in Ruby,  you’ll find it eventually  leads back to Exception.  Here, I’ll prove it to you. These are most of Ruby’s built-in  exceptions, displayed hierarchically:

Exception
   NoMemoryError
   ScriptError
       LoadError
       NotImplementedError
       SyntaxError
    SecurityError
    SignalException
        Interrupt
     StandardError
        ArgumentError
        UncaughtThrowError
        EncodingError
        CompatibilityError
        ConverterNotFoundError
        InvalidByteSequenceError
        UndefinedConversionError
        FiberError
        IOError
           EOFError
        IndexError
        KeyError
        StopIteration
        LocalJumpError
        NameError
           NoMethodError
        RangeError
           FloatDomainError
        RegexpError
        RuntimeError
        SystemCallError
        ThreadError
        TypeError
        ZeroDivisionError
SystemExit
SystemStackError
fatal – impossible to rescue

Rescuing errors of a specific class also rescues errors of its child classes.For example, when you rescue StandardError , you not only rescue exceptions with class StandardError but those of its children as well. If you look at the chart, you’ll see this includes ArgumentError,IOError, and many more.

begin
 #do something
rescue Exception => e
end

Rescuing All Exceptions (The Wrong Way)

begin
 #do something
rescue Exception => e
end

The code above will rescue every exception.Don’t do it! It’ll break your program in weird ways.

That’s because Ruby uses exceptions for things other than errors. It also uses them to handle messages from the operating system called “Signals.” If you’ve ever pressed “ctrl-c” to exit a program, you’ve used a signal. By suppressing all exceptions, you also suppress those signals.

There are also some kinds of exceptions — like syntax errors — that really should cause your program to crash. If you suppress them, you’ll never know when you make typos or other mistakes.


Rescuing All Errors (The Right Way)


Go back and look at the class hierarchy chart and you’ll see that all of the errors you’d want to rescue are children of StandardError.

That means that if you want to rescue “all errors” you should rescue StandardError.

begin
 do_something
rescue StandardError => e
 # Only your app's exceptions are swallowed. Things like SyntaxError are left alone.
end

In fact, if you don’t specify an exception class, Ruby assumes you mean StandardError

begin
  do_something
rescue => e
  # This is the same as rescuing StandardError
end


Advanced Rescue & Raise

Full Rescue Syntax

you have a situation that requires a more sophisticated approach to rescue an exception. Below is an example of Ruby’s full rescue syntax.

begin
 do_someting
rescue TooHotError => too_hot
 # This code is run if a TooHotError occurs
rescue TooColdError => too_cold
 # This code is run if a TooColdError occurs
else
 # This code is run if no exception occurred at all
ensure
 # This code is always run, regardless of whether an exception occurred
end

• else - is executed when no exceptions occur at all.
• ensure - is always executed, even if exceptions did occur. This is really
   useful for actions like closing files when something goes wrong.
• rescue - you know what rescue, is, but I just wanted to point out that
   we’re usingmultiple rescues to provide different behavior for different
   exceptions.

 begin
request_web_page
 rescue TimeoutError
  retry_request
 rescue
send_alert
 else
record_success
 ensure
close_network_connection
 else
 
Here, we respond to timeout exceptions by retrying. All other exceptions trig-
ger alerts. If no error occurred, we save the success in the database. Finally,
regardless of what else occurred, we always close the network connection.