Exceptions and execution are always tied together. If you open a file that doesn't exist and you don't handle it properly, then 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 letting the program stop completely.
Ruby provides a perfect mechanism for handling exceptions. We can attach code that might throw an exception to the begin/end block and tell Ruby exactly what type of exception to handle with the rescue clause.
Everything from begin to rescue is protected. If an exception occurs during the execution of the code block, control passes 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 currently thrown exception type, or the parent of the exception, the match is successful.
If the exception does not match all of the specified error types, we can use an else clause after all rescue clauses.
The above example runs the output as . As you can see, STDIN replaces file because open failed.
Using 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.
The following is the process flow:
- An exception occurred while opening.
- Jump to rescue. Fname is reassigned.
- Jump through retry to the beginning of begin.
- This file was successfully opened.
- Continue the basic process.
Note: If the renamed file does not exist, this example code will try infinitely. So use retry with caution when handling exceptions.
Use the raise statement
You can throw an exception using the raise statement. The following method throws an exception when called. Its second message will be output.
The first form simply rethrows the current exception (and throws a RuntimeError if there is no current exception). This is used in exception handlers that need to interpret exceptions before passing in exceptions.
The second form creates a new RuntimeError exception, setting its message to the given string. The exception is thrown to the call stack.
The third form creates an exception with the first argument and then sets the associated message to the second argument.
The fourth form is similar to the third form, and you can add any additional conditional statements (such as unless) to throw an exception.
The above example runs the output as:
I am before the raise . I am rescued. I am after the begin block.
Another example of a raise usage:
The above example runs the output as:
A test exception. ["main.rb:4"]< /pre>
Using the ensure statement
Sometimes, whether or not you throw an exception, you need to ensure that some processing is done at the end of the code block. For example, you might have opened a file when you entered, and when you exit the block, you need to make sure to close the file.The
ensure clause is this. 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 it terminates with a block, whether it throws and handles an exception, or if it terminates with an uncaught exception. The ensure block will always run.
Syntaxbegin #.. Process #.. Throw an exception Span> rescue #.. Handling errors ensure #.. Finally make sure to execute Span> #.. This will always be performed end
Instancebegin raise 'A test exception.' rescue Exception => e puts e.message puts e.backtrace.inspect ensure puts "Ensuring execution" end
The above example runs the output as:A test exception. ["main.rb:4"] Ensuring execution
Use the else statement
If the else clause is provided, it is usually placed after the rescue clause, before any ensure.The body of the
else clause is only executed if the code body does not throw an exception.
Syntaxbegin #.. Process #.. Throw an exception Span> rescue #.. Handling errors else #.. Execute if there are no exceptions ensure #.. Finally make sure to execute Span> #.. This will always be performed end
Instancebegin # 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 above example runs the output as:I'm not raising exception Congratulations-- no errors! Ensuring execution
Use the $! variable to catch the thrown error message.
Catch and Throw
Raise and rescue exception mechanisms can abandon execution when an error occurs, and sometimes need to jump out of some deeply nested structures during normal processing. At this point catch and throw come in handy.
catch defines a block that uses the given name (which can be a Symbol or a String) as a label. The block will execute normally until it encounters a throw.
grammarthrow :lablename #.. This will not be executed catch :lablename do #.. Encounter one throw After the match will be executed catch end or throw :lablename condition #..This will not be executed after a throw is encountered. catch :lablename do #.. The match will be executed after a throw is encountered catch end
In the example below, if the user types '!' to respond to any prompts, use a throw to terminate the interaction with the user.
Instancedef 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: ") # .. # Processing information end promptAndGet("Name:")
The above program requires human interaction and you can try it on your computer. The output of the above example runs as follows:Name: Ruby on Rails Age: 3 Sex: ! Name:Just Ruby
Ruby's standard classes and modules throw exceptions. All exception classes form a hierarchy, including the top Exception class. The next layer is in seven different types:
Fatal is another exception in this layer, but the Ruby interpreter only uses it internally.
ScriptError and StandardError have some subclasses, but we don't need to know these details here. The most important thing is to create our own exception classes, which must be subclasses of the class Exception or its children.
Let's see an example:
Now, look at the example below, which will use the above exception:
The most important line here is raise FileSaveError.new($!). We call raise to indicate that an exception has occurred, passing it to a new instance of FileSaveError , which caused the data to fail due to a specific exception.