Ruby exception is an object, an instance of the class Exception or descendent of that class. It represents some exceptional condition.
In a Ruby program, when something goes wrong, it throws an exceptional behavior. By default Ruby program terminates on throwing an exception.
We can declare some exception handlers within Ruby. An exception handler is a block of code which is executed when exception occurs in some other block of code.
Exceptions are handled in two ways. Either you can terminate the program or deal with the exception. To deal with an exception, you can provide a rescue clause. By providing this, program control flows to the rescue clause.
When an exception is raised but not handled, global variable $! contains the current exception and $@ contains the current exception’s backtrace.
Ruby predefined classes like Exception and its children helps you to handle errors of your program. In Ruby exception hierarchy, most of the sub classes extend class StandardError. These are the normal exceptions.
Ruby Class Exceptions
Built-in subclasses of exception are as follows:
- NoMemoryError
- ScriptError
- SecurityError
- SignalException
- StandardError
- SystenExit
- SystemStackError
- fatal – impossible to rescue
Example:
def raise_exception
puts 'I am before the raise.'
raise 'oops! An error has occured'
puts 'I am after the raise'
end
raise_exception
Output:
The raise method comes from the Kernel module.
Handling an Exception
To handle exception, the code that raises exception is enclosed within begin-end block. Using rescue clauses we can state type of exceptions we want to handle.
Example:
def raise_and_rescue
begin
puts 'Before the raise.'
raise 'An error occured.'
puts 'After the raise.'
rescue
puts 'Code rescued.'
end
puts 'After the begin block.'
end
raise_and_rescue
Output:
In the above example, interrupted code does not run completely. After exception handling code resumes after the begin-end block.
If no argument is defined in the rescue clause, the parameter defaults to StandardError. Each rescue clause specify multiple exceptions to catch. If raise is used without any parameters, exception may re-raised.
The rescue clauses are written in a begin/rescue block. Exceptions if not handled by one rescue clause will br handled with the next one.
begin
code..
rescue OneTypeOfException
code..
rescue AnotherTypeOfException
code..
else
# Other exceptions
end
In the begin block, each rescue clause with the raised exception will be compared against each of parameters in turn. It will be matched when the type of error thrown and exception named in the rescue clause is either same or is a superclass of that exception. The else clause is executed if body of begin statement is completed without exceptions. If an exception occurs, else clause will not be executed.
Exception Object
Exception objects are normal objects. A rescued exception can be hold to a variable within the rescue clause.
Example:
begin
raise 'an exception'
rescue ZeroDivisionError => e
puts "Exception Class: #{ e.class.name }"
puts "Exception Message: #{ e.message }"
puts "Exception Backtrace: #{ e.backtrace }"
end
The Exception class defines two methods that return details about the exception. The message method returns a string that define the explanation of error. The backtrace method returns an array of string that represent the call stack at that point where exception was raised.
Using retry Statement
Usaually in a rescue clause, the exception is captured and code resumes after begin block. Using retry statement, the rescue block code can be resumed from begin after capturing an exception.
Syntax:
begin
code....
rescue
# capture exceptions
retry # program will run from the begin block
end
Example:
#!/usr/bin/ruby
begin
x = Dir.mkdir "alreadyExist"
if x
puts "Directory created"
end
rescue
y = "newDir"
retry
end
The above program runs as follows:
Step 1 In the begin block, code is written to make a directory that already exists.
Step 2 This will throw an error.
Step 3 In rescue block, y was reassigned.
Step 4 The retry statement will go to the begin block.
Step 5 Directory will be created.
Using raise Statement
The raise statement is used to raise an exception.
Syntax:
raise
Or,
- raise “Error Message”
Or,
raise ExceptionType, "Error Message"
Or,
raise ExceptionType, "Error Message" condition
The first one re-raises the current exception. It is used for exception handlers where exception is intercepted before passing it on.
The second one creates a new RuntimeError exception. This exception is then raised up the call stack.
The third one uses first argument to create an exception, then sets associated message to the second argument.
The fourth one similar to third one. In this you can add any conditional statement to raise an exception.
Example:
#!/usr/bin/ruby
begin
puts 'code before raise.'
raise 'exception occurred.'
puts 'code after raise.'
rescue
puts 'I am rescued.'
end
puts 'code after begin block.'
Output:
Using ensure Statement
There is an ensure clause which guarantees some processing at the end of code. The ensure block always run whether an exception is raised or not. It is placed after last rescue clause and will always executed as the block terminates.
The ensure block will run at any case whether an exception arises, exception is rescued or code is terminated by uncaught exception.
Syntax:
begin
code..
#..raise exception
rescue
#.. exception is rescued
ensure
#.. This code will always execute.
end
Example:
begin
raise 'Exception'
rescue Exception => e
puts e.message
puts e.backtrace.inspect
ensure
puts "The ensure code will always run"
end
Output:
Using else Statement
The else clause is always present after rescue clause and before ensure clause. If no exceptions are raised, then only else block is executed.
Syntax:
begin
code..
#..raise exception
rescue
# .. exception is rescued
else
#.. executes if there is no exception
ensure
#.. This code will always execute.
end
Example:
- begin
- # raise 'A test exception.'
- puts "no exception is raised"
- rescue Exception => e
- puts e.message
- puts e.backtrace.inspect
- else
- puts "else code will be executed as no exception is raised."
- ensure
- puts "ensure code will run"
- end
Output:
Ruby Catch and Throw
Ruby catch and throw provide a way to jump from the execution early when no further work is needed in a code.
The catch defines a block that is labeled with a given name. It is used to jump out of nested code. Using catch, the block will be executed normally until throw is encountered.
The catch and throw method is faster than rescue and raise clauses. Hence, it is more suitable to use.
Syntax:
throw :lablename
#.. this code will not be executed
catch :lablename do
#.. matching catch will be executed after a throw is encountered.
end
Or,
throw :lablename condition
#.. this code will not be executed
catch :lablename do
#.. matching catch will be executed after a throw is encountered.
end
Example:
def promptAndGet(prompt)
print prompt
res = readline.chomp
throw :quitRequested if res == "!"
return res
end
catch :quitRequested do
name = promptAndGet("Name: ")
age = promptAndGet("Occupation: ")
# ..
# process information
end
promptAndGet("Name:")
Output:
Leave a Reply