Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Ruby exception


May 12, 2021 Ruby


Table of contents


Ruby exception

Exceptions and execution are always linked. If you open a file that doesn't exist and don't handle it properly, your program is considered low-quality.

If an exception occurs, the program stops. Exceptions are used to handle various types of errors that can occur during program execution, so take appropriate action without stopping the program completely.

Ruby provides a perfect mechanism for handling exceptions. We can attach code to the begin/end block that might throw exceptions and use the rescue clause to tell Ruby the type of exception that is perfect to handle.

Grammar

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

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

For each rescue clause in the begin block, Ruby compares the thrown exceptions in turn with each argument. If the exception named in the rescue clause is the same as the currently thrown exception type, or if the exception's parent class, the match succeeds.

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

#!/usr/bin/ruby

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

This will result in the following results. As you can see, STDIN replaces file because Open failed.

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

Use the retry statement

You can use the rescue block to catch exceptions, and then use the retry statement to execute the begin block from the beginning.

Grammar

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

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

Here's how to handle it:

  • An exception occurred when it was opened.
  • Jump to rescue. fname is reassigned.
  • Jump to the beginning of begin with retry.
  • This time the file opened successfully.
  • Continue the basic process.

Note: If the renamed file does not exist, this Force code will attempt indefinitely. So when handling exceptions, use retry with caution.

Use the raise statement

You can use the raise statement to throw an exception. T he following method throws an exception at the time of the call. Its second message will be output.

Grammar

raise 

OR

raise "Error Message" 

OR

raise ExceptionType, "Error Message"

OR

raise ExceptionType, "Error Message" condition

The first form simply throws the current exception again (throw a RuntimeError if there is no current exception). This is used in exception handlers that need to interpret exceptions before they can be passed in.

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

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

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

#!/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.'  

This results in the following:

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

Another example of the use of raise:

#!/usr/bin/ruby

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

This results in the following:

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

Use the ensure statement

Sometimes, whether you throw an exception or not, you need to make sure that some processing is done at the end of the block. For example, you might open a file when you enter, and when you exit the block, you need to make sure that the file is closed.

That's what the ensure clause does. T he ensure is placed after the last rescue clause and contains a block of code that is always executed when the block terminates. It has nothing to do with whether the block exits properly, whether it throws and handles exceptions, whether it terminates due to an unaught exception, and the ensure block always runs.

Grammar

begin 
   #.. 过程
   #.. 抛出异常
rescue 
   #.. 处理错误 
ensure 
   #.. 最后确保执行
   #.. 这总是会执行
end
begin
  raise 'A test exception.'
rescue Exception => e
  puts e.message
  puts e.backtrace.inspect
ensure
  puts "Ensuring execution"
end

This results in the following:

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

Use the else statement

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

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

Grammar

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

This results in the following:

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

Use $! Variables can catch thrown error messages.

Catch and Throw

The exception mechanisms of raise and rescue can discard execution in the event of an error, sometimes requiring some deep nested structure to jump out of normal processing. Catch and throw come in play at this point.

Catch defines a block that uses a given name, which can be Symbol or String, as a label. The block will execute normally knowing that it encountered a throw.

Grammar

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

OR

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

In the following example, if the user types '!' in response to any prompt, use a throw 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, which you can try on your computer. This results in the following:

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

Class Exception

Ruby's standard classes and modules throw exceptions. A ll exception classes form a hierarchy, including the Exception class at the top. The next layer is seven different types:

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

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

Both ScriptError and StandardError have sub-classes, but we don't need to know the details here. The most important thing is to create our own exception classes, which must be exceptions or sub-classes of their children.

Let's look at an example:

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

Now, look at the following example and you'll use the above exception:

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 and pass it on to a new instance of FileSaveError, which causes the data write to fail due to a particular exception.