Ruby Exceptions

Exceptions and execution are always linked. If you open a file that does not exist and do not handle this situation properly, your program is considered 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 action can be taken without letting the program stop completely.

Ruby provides a perfect mechanism for handling exceptions. We canbegin/endattach code that may raise an exception in a block, and userescueclause to tell Ruby exactly which exception types to handle.

Syntax

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

frombegintorescueEverything inside is protected. If an exception occurs during execution of the block, control is passed torescueandendthe block between.

Forbegineach in the blockrescueclause, Ruby compares the raised exception with each parameter in turn. If the exception named in a rescue clause is the same type as the currently raised exception, or is a parent class of that exception, then the match succeeds.

If the exception does not match all specified error types, we can at all therescueafter the clauses use aelseclause.

Examples

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

The above example outputs the result. You can see,STDINreplacedfile, becauseopeningfailed.

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

UsingretryStatement

You can userescuea block to catch exceptions, then useretrystatement to execute from the beginningbeginblock.

Syntax

begin #The exception raised 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 to the beginning of begin end

Examples

#!/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:

  • An exception occurs when opening.
  • Jump to rescue. fname is reassigned.
  • Jump to the beginning of begin via retry.
  • This time the file opens successfully.
  • Continue with the basic process.

Note:If the renamed file does not exist, this example code will try infinitely. So when handling exceptions, use with cautionretry。

UsingraiseStatement

You can useraisestatement to raise an exception. The following method raises an exception when called. Its second message will be output.

Syntax

raiseorraise "Error Message"orraise ExceptionType, "Error Message"orraise ExceptionType, "Error Message" condition

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

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

The third form creates an exception using the first parameter, then sets the associated message to the second parameter.

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

Examples

#!/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 above example outputs:

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

Another demonstrationraiseexample of usage:

Examples

#!/usr/bin/ruby begin raise 'A test exception.' rescue Exception => e puts e.message puts e.backtrace.inspect end

The above example outputs:

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

UsingensureStatement

Sometimes, whether or not an exception is raised, you need to ensure that some processing is completed when the code block ends. For example, you may have opened a file on entry, and when you exit the block, you need to make sure the file is closed.

ensureThe ensure clause does exactly this. ensure is placed after the last rescue clause and contains a block of code that is always executed when the block terminates. It does not matter whether the block exits normally, whether it raises and handles an exception, or whether it terminates due to an uncaught exception,ensurethe block will always run.

Syntax

begin #.. process #.. raise an exception rescue #.. handle error ensure #.. finally ensure execution #.. this will always execute end

Examples

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

The above example outputs:

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

UsingelseStatement

If aelseclause is provided, it is generally placed after therescueclause, before anyensurebefore.

elseThe body of the clause is executed only when the main body of the code does not raise an exception.

Syntax

begin #.. process #.. raise an exception rescue #.. handle error else #.. execute if no exception ensure #.. finally ensure execution #.. this will always execute end

Examples

begin #raise '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 above example outputs:

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

Using the $! variable, you can capture the raised error message.

Catch and Throw

The exception mechanism of raise and rescue can abandon 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.

catchdefines a block that uses the given name (which can be a Symbol or String) as a label. The block executes normally until it encounters a throw.

Syntax

throw :lablename #.. this will not be executed catch :lablename do #.. catch that will be executed after encountering a throw endorthrow :lablename condition #.. this will not be executed catch :lablename do #.. catch that will be executed after encountering a throw end

Examples

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

Examples

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: ") # .. #Handling information end promptAndGet("Name:")

The above program requires human interaction; you can try it on your computer. The above example outputs:

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

Class Exception

Ruby's standard classes and modules raise exceptions. All exception classes form a hierarchy, including the Exception class at the top. The next level down consists of seven different types:

  • Interrupt
  • NoMemoryError
  • SignalException
  • ScriptError
  • StandardError
  • SystemExit

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

Both ScriptError and StandardError have some subclasses, but we do not need to understand those details here. The most important thing is to create our own exception classes, which must be subclasses of the Exception class or its descendants.

Let's look at an example:

Examples

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:

Examples

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

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

Other extensions