May 12, 2021 Ruby
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.
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>
You can use the rescue block to catch exceptions, and then use the retry statement to execute the begin block from the beginning.
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:
Note: If the renamed file does not exist, this Force code will attempt indefinitely. So when handling exceptions, use retry with caution.
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.
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"]
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.
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
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.
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.
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.
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
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:
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.