Before talking about the exception handling mechanisms, lets first delve into what an exception is. Exceptions by definition are the unwanted or unexpected event, that occurs during the execution of a program, i.e. at run time, which disrupts the normal flow of the program’s instructions.
In Ruby, an exception is a special kind of object, an instance of the
class Exception or a descendant of that class that
represents some kind of exceptional condition; it indicates that something has gone wrong. Ruby has some predefined classes -
Exception and its children - that help you to handle errors that can occur in your program. Some common exceptions are:
RuntimeError(this is the default exception raised by the raise method), StandardError, NoMethodError, NameError, IOError, TypeError and ArgumentError.
The following figure shows the
Ruby exception hierarchy.
Fig: Exception hierarchy in Ruby
The chart above shows that most of the subclasses extend a class known as StandardError. These are the “normal” exceptions that typical Ruby programs try to handle. The other exceptions represent lower-level, more serious, or less recoverable conditions, and normal Ruby programs do not typically attempt to handle them.
Generally, exception handling is a process which describes a way to handle the error raised in a program.The code in which an exception is raised, is enclosed between the begin/end block, so you can use a rescue clause to handle this type of exception. In other words, to perform exception handling in Ruby, we enclose the code that could raise an exception in a begin-end block and use one or more rescue clauses to tell Ruby the types of exceptions we want to handle. It is to be noted that the body of a method definition is an implicit begin-end block; the begin is omitted, and the entire body of the method is subject to exception handling, ending with the end of the method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 def raise_and_rescue # suspection of exception begin puts 'This is Before Exception Arise!' # using raise to create an exception raise 'Exception Created!' puts 'After Exception' # using Rescue method rescue puts 'Finally Saved!' end puts 'Outside from Begin Block!' end
1 2 3 This is Before Exception Arise! Finally Saved! Outside from Begin Block!
Observe that the code interrupted by the exception never gets run. Once the exception is handled, execution continues immediately after the begin block that spawned it.
If you write a rescue clause with no parameter list, the parameter defaults to StandardError. Each rescue clause can specify multiple exceptions to catch. Multiple rescue clauses can be used in the same program which means if an exception is not handled by the first rescue clause, then another rescue clause will definitely handle the exception. If no rescue clause match, or if an exception occurs outside the begin/end block, then Ruby moves up to the stack and looks for an exception handler in the caller.
1 2 3 4 5 6 7 8 9 begin # - rescue OneTypeOfException # - rescue AnotherTypeOfException # - else # No exceptions end
For each rescue clause in the begin block, Ruby compares the raised Exception against each of the parameters in turn. The match will succeed if the exception named in the rescue clause is the same as the type of the currently thrown exception, or is a superclass of that exception. The code in an else clause is executed if the code in the body of the begin statement runs to completion without exceptions. If an exception occurs, then the else clause will obviously not be executed. The use of an else clause is not particularly common in Ruby.
Terms used in Exception Handling
The Exception class defines two methods that return details about the exception. The message method returns a string that may provide human-readable details about what went wrong. The other important method is backtrace. This method returns an array of strings that represent the call stack at the point that the exception was raised.
1) retry statement:
This statement is used to execute the rescue block again from the beginning after capturing the exception. Note: Be careful while using retry statement because it may result into infinite loop.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # Ruby program to illustrate # use of retry statement begin # using raise to create an exception raise 'Exception Created!' puts 'After Exception' # using Rescue method rescue puts 'Finally Saved!' # using retry statement retry end
1 2 3 4 5 6 7 8 9 10 Finally Saved! Finally Saved! Finally Saved! Finally Saved! Finally Saved! Finally Saved! . . . .
2) raise statement:
This statement is used to raise an exception.
raise : This syntax is used to re-raise the current exception. It is generally used by the exception handler where an exception is
interrupted before passing it on.
raise "Error Message" : This syntax is used to create a RuntimeError exception and it raises up the call stack.
raise ExceptionType, "Error Message": In this syntax, the first argument is used to create an exception and then set the message in
the second argument.
raise ExceptionType, "Error Message" condition : In this syntax, the first argument is used to create an exception and then
set the message in the second argument. You can also set a condition statement to raise an exception.
1 2 3 4 5 6 7 8 9 10 11 12 13 # Ruby program to illustrate # use of raise statement begin puts 'This is Before Exception Arise!' # using raise to create an exception raise 'Exception Created!' puts 'After Exception' end
1 2 This is Before Exception Arise! Exception Created!
3) ensure statement:
If you need the guarantee that some processing is done at the end of a block of code, regardless of whether an exception was raised then the ensure clause can be used. ensure goes after the last rescue clause and contains a chunk of code that will always be executed as the block terminates. The ensure block will always run.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # Ruby program to illustrate # use of ensure statement begin # using raise to create an exception raise 'Exception Created!' puts 'After Exception' # using Rescue statement rescue puts 'Finally Saved!' # using ensure statement ensure puts 'ensure block execute' end
1 2 Finally Saved! ensure block execute
4) else statement:
This statement is present in between the rescue block and ensure block. This block only executes when no exception is raised.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 # Ruby program to illustrate # use of else statement begin # using raise to create an exception # raise 'Exception Created!' puts 'no Exception raise' # using Rescue method rescue puts 'Finally Saved!' # using else statement else puts 'Else block execute because of no exception raise' # using ensure statement ensure puts 'ensure block execute' end
1 2 3 no Exception raise Else block execute because of no exception raise ensure block execute
5) catch and throw
In Ruby, catch and throw blocks are the lightweight mechanism for error handling and used to jump from the exception when there is no additional work is available in the program. catch and throw method will work faster than raise and rescue method. The catch block is used to jump out from the nested block and the block is labeled with a name. This block works normally until it encounters with the throw block. When a throw statement is encountered Ruby will look up the call stack for a catch statement with the corresponding symbol. It will then unwind the call stack up to that point and terminate the block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # Ruby program to illustrate # use of catch and throw statement # defining a method def catch_and_throw(value) puts value a = readline.chomp # using throw statement throw :value_e if a == "!" return a end # using catch statement catch :value_e do # enter number number = catch_and_throw("Enter Number: ") end
1 2 Enter Number: 1 1
1 2 Enter Number: ! nil
In the above example the
catch_and_throw method is used to print the value enter in the catch statement. If the user enters a number, then
it will print number, but if the user enters !, then it will give you nil. Because the throw statement has effectively never executed.