Easy Tutorial
❮ Ruby Syntax Ruby Installation Unix ❯

Ruby Exceptions

Exceptions and execution are always linked together. If you try to open a non-existent file and do not handle this situation properly, your program is considered to be of low quality.

If an exception occurs, the program stops. Exceptions are used to handle various types of errors that may occur during program execution, so appropriate actions can be taken without causing the program to completely halt.

Ruby provides a perfect mechanism for handling exceptions. We can attach code that may throw exceptions within a begin/end block and use the rescue clause to tell Ruby the types of exceptions we want to handle.

Syntax

begin # Start

 raise.. # Throw exception

rescue [ExceptionType = StandardException] # Catch specified type of exception, default is StandardException
 $! # Represents exception information
 $@ # Represents the code location where the exception occurred
else # Other exceptions
 ..
ensure # Regardless of whether an exception occurs, enter this code block

end # End

Everything from begin to rescue is protected. If an exception occurs during the execution of the code block, control is passed to the block between rescue and end.

For each rescue clause in the begin block, Ruby compares the thrown exception with each parameter in turn. If the exception named in the rescue clause is the same as the current thrown exception type or a parent class of it, the match is successful.

If the exception does not match any specified error types, we can use an else clause after all the rescue clauses.

Example

#!/usr/bin/ruby

begin
   file = open("/unexistant_file")
   if file
      puts "File opened successfully"
   end
rescue
      file = STDIN
end
print file, "==", STDIN, "\n"

The output result of the above example is as follows. You can see that STDIN replaces file because the open operation failed.

#<IO:0xb7d16f84>==#<IO:0xb7d16f84>

Using the retry Statement

You can use the rescue block to catch exceptions and then use the retry statement to restart execution from the beginning of the begin block.

Syntax

begin
    # Exceptions thrown by this code will be caught by the following rescue clause
rescue
    # This block will catch all types of exceptions
    retry  # This will move control back to the beginning of begin
end

Example

#!/usr/bin/ruby

begin
   file = open("/unexistant_file")
   if file
      puts "File opened successfully"
   end
rescue
   fname = "existant_file"
   retry
end

The following is the processing flow:

Note: If the renamed file does not exist, this example code will attempt indefinitely. Therefore, use retry cautiously when handling exceptions.

Using the raise Statement

You can use the raise statement to throw exceptions. The following method throws an exception when called. Its second message will be output.

Syntax

raise 

or

raise "Error Message" 

or

raise ExceptionType, "Error Message"

or

raise ExceptionType, "Error Message" condition

The first form simply rethrows the current exception (or a RuntimeError if there is no current exception). This is used in exception handlers that need to interpret the exception before passing it on.

The second form creates a new RuntimeError exception, setting its message to the given string. This exception is then thrown up the call stack.

The third form creates an exception using the first parameter and sets the related message to the second parameter.

The fourth form is similar to the third form, and you can add any additional conditional statements (such as unless) to throw the exception.

Example

#!/usr/bin/ruby

begin  
    puts 'I am before the raise.'  
    raise 'An error has occurred.'  
    puts 'I am after the raise.'  
rescue  
    puts 'I am rescued.'  
end  
puts 'I am after the begin block.'

The output result of the above example is:

I am before the raise.  
I am rescued.  
I am after the begin block.

Another example demonstrating the use of raise:

Example

#!/usr/bin/ruby

begin  
  raise 'A test exception.'  
rescue Exception => e  
  puts e.message  
  puts e.backtrace.inspect  
end

The output result of the above example is:

A test exception.
["main.rb:4"]

Using the ensure Statement

Sometimes, regardless of whether an exception is thrown, you need to ensure that some processing is completed at the end of the code block. For example, you might open a file when entering and need to ensure it is closed when exiting the block.

The ensure clause does this. Ensure is placed after the last rescue clause and contains a block of code that always executes when the block terminates. It doesn't matter whether the block exits normally, throws and handles an exception, or terminates due to an uncaught exception; the ensure block always runs.

Syntax

begin 
   #.. process
   #.. throw exception
rescue 
   #.. handle error 
ensure 
   #.. final ensure execution
   #.. this always executes
end

Example

begin
  raise 'A test exception.'
rescue Exception => e
  puts e.message
  puts e.backtrace.inspect
ensure
  puts "Ensuring execution"
end

The output result of the above example is:

A test exception.
["main.rb:4"]
Ensuring execution

Using the else Statement

If an else clause is provided, it is usually placed after the rescue clause and before any ensure.

The body of the else clause executes only if the code body does not throw an exception.

Syntax

begin 
   #.. process 
   #.. throw exception
rescue 
   #.. handle error
else
   #.. execute if no exceptions
ensure 
   #.. final ensure execution
   #.. this always executes
end

Example

begin
 # Throw 'A test exception.'
 puts "I'm not raising exception"
rescue Exception => e
  puts e.message
  puts e.backtrace.inspect
else
   puts "Congratulations-- no errors!"
ensure
  puts "Ensuring execution"
end

The output result of the above example is:

I'm not raising exception
Congratulations-- no errors!
Ensuring execution

The $! variable can be used to catch the thrown error message.

Catch and Throw

The raise and rescue exception mechanism aborts execution when an error occurs. Sometimes, you need to jump out of some deeply nested structures during normal processing. This is where catch and throw come in handy.

catch defines a block that uses a given name (either a Symbol or String) as a label. The block executes normally until a throw is encountered.

Syntax

throw :lablename
#.. this will not be executed
catch :lablename do
#.. catch will be executed after a throw is encountered
end

or

throw :lablename condition
#.. this will not be executed
catch :lablename do
#.. catch will be executed after a throw is encountered
end

Example

In the following example, if the user types '!' in response to any prompt, a throw terminates the interaction with the user.

Example

def promptAndGet(prompt)
   print prompt
   res = readline.chomp
   throw :quitRequested if res == "!"
   return res
end

catch :quitRequested do
   name = promptAndGet("Name: ")
   age = promptAndGet("Age: ")
   sex = promptAndGet("Sex: ")
   # ..
   # process information
end
promptAndGet("Name:")

The above program requires manual interaction, which you can try on your computer. The output result of the above example is:

Name: Ruby on Rails
Age: 3
Sex: !
Name:Just Ruby

Class Exception

Ruby's standard classes and modules throw exceptions. All exception classes form a hierarchy, including the top-level Exception class. The next level comprises seven different types:

Fatal is another exception at this level, but the Ruby interpreter only uses it internally.

ScriptError and StandardError both have some subclasses, but we don't need to know the details here. The most important thing is to create our own exception classes, which must be subclasses of Exception or its descendants.

Let's look at an example:

Example

class FileSaveError < StandardError
   attr_reader :reason
   def initialize(reason)
      @reason = reason
   end
end

Now, look at the following example, which will use the above exception:

Example

File.open(path, "w") do |file|
begin
    # Write data ...
rescue
    # Error occurred
    raise FileSaveError.new($!)
end
end

Here, the most important line is raise FileSaveError.new($!). We call raise to signal that an exception has occurred, passing it a new instance of FileSaveError, caused by a specific exception leading to a data write failure.

❮ Ruby Syntax Ruby Installation Unix ❯