Ruby exception


Exceptions and execution are always linked together. If you open a file that doesn't exist and don't handle the situation appropriately, 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 is taken without bringing the program to a complete halt.

Ruby provides a perfect mechanism for handling exceptions. We can enclose the code that may throw an exception in a begin/end block and use the rescue clause to tell Ruby what type of exception to handle.

Syntax

begin #开始
 
 raise.. #抛出异常
 
rescue [ExceptionType = StandardException] #捕获指定类型的异常 缺省值是StandardException
 $! #表示异常信息
 $@ #表示异常出现的代码位置
else #其余异常
 ..
ensure #不管有没有异常,进入该代码块
 
end #结束

Everything from begin to rescue is protected. If an exception occurs during the execution of a 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 argument in turn. If the exception named in the rescue clause is of the same type as the currently thrown exception, or is the parent class of that exception, the match is successful.

If the exception does not match all specified error types, we can use an else clause after all 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. You can see that STDIN replaces file because failed to open .

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

Use the retry statement

You can use the rescue block to catch the exception and then use the retry statement to start execution from the beginning begin Block.

Syntax

begin
    # 这段代码抛出的异常将被下面的 rescue 子句捕获
rescue
    # 这个块将捕获所有类型的异常
    retry  # 这将把控制移到 begin 的开头
end

Instance

#!/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 occurred while opening.

  • Jump to rescue. fname is reassigned.

  • Jump to the beginning of begin through retry.

  • This time the file was opened successfully.

  • Continue with the basic process.

Note: If the renamed file does not exist, the code of this force will try infinitely. Therefore, use retry with caution when handling exceptions.

Using the raise statement

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

Syntax

raise 

或

raise "Error Message" 

或

raise ExceptionType, "Error Message"

或

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 in.

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

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

The fourth form is similar to the third form, you can add any additional conditional statements (such as unless) to throw an 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 raise usage:

#!/usr/bin/ruby

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

Above The output result of the instance running is:

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

Use 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 have a file open when you enter, and when you exit the block, you need to make sure you close the file.

ensure This is what the clause does. ensure is placed after the last rescue clause and contains a block of code that is always executed when the block terminates. It doesn't matter whether the block exits gracefully, whether an exception is thrown and handled, or whether it terminates with an uncaught exception, the ensure block will always run.

Grammar

begin 
   #.. 过程
   #.. 抛出异常
rescue 
   #.. 处理错误 
ensure 
   #.. 最后确保执行
   #.. 这总是会执行
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

Use else statement

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

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

Syntax

begin 
   #.. 过程 
   #.. 抛出异常
rescue 
   #.. 处理错误
else
   #.. 如果没有异常则执行
ensure 
   #.. 最后确保执行
   #.. 这总是会执行
end

Examples

begin
 # 抛出 '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 of the above example is:

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

Use the $! variable to capture the error message thrown.

Catch and Throw

The exception mechanism of raise and rescue can give up execution when an error occurs. Sometimes it is necessary 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 the given name (can be a Symbol or String) as a label. The block will execute normally until a throw is encountered.

Syntax

throw :lablename
#.. 这不会被执行
catch :lablename do
#.. 在遇到一个 throw 后匹配将被执行的 catch
end

或

throw :lablename condition
#.. 这不会被执行
catch :lablename do
#.. 在遇到一个 throw 后匹配将被执行的 catch
end

Examples

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

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: ")
   # ..
   # 处理信息
end
promptAndGet("Name:")

The above program requires human interaction, you can try it 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 Exception class at the top. The next level down are seven different types:

  • Interrupt

  • NoMemoryError

  • SignalException

  • ScriptError

  • StandardError

  • SystemExit

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

Both ScriptError and StandardError have some subclasses, but we don’t need to know those details here. The most important thing is to create our own exception classes, they must be subclasses of class Exception or its descendants.

Let us look at an example:

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

Now, look at the following example, where the above exception will be used:

File.open(path, "w") do |file|
begin
    # 写出数据 ...
rescue
    # 发生错误
    raise FileSaveError.new($!)
end
end

Here, the most important line is raise FileSaveError.new($!). We call raise to indicate that an exception has occurred, passing it to a new instance of FileSaveError that failed to write data due to the specific exception.