YouTip LogoYouTip

Ruby Exceptions

Exceptions are always associated with execution. If you open a file that doesn't exist and don't handle this 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 actions can be taken without causing the program to stop completely. Ruby provides a perfect mechanism for handling exceptions. We can attach code that might throw exceptions within a _begin/end_ block and use _rescue_ clauses to tell Ruby exactly which types of exceptions to handle. ## Syntax begin raise.. rescue $! $@ else .. ensure .. Everything from _begin_ to _rescue_ is protected. If an exception occurs during the execution of the 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 parameter in turn. If the exception named in the rescue clause is the same type as the currently thrown exception, or is a parent class of that exception, the match is successful. If the exception doesn't match any of the specified error types, we can use an _else_ clause after all the _rescue_ clauses. ## Example begin file = open("/unexistant_file") if file puts "File opened successfully" end rescue file = STDIN end print file, "==", STDIN, "n" The output of the above example is. You can see that _STDIN_ replaced _file_ because the _open_ failed. #==# You can use a _rescue_ block to catch an exception, then use the _retry_ statement to start executing the _begin_ block from the beginning. ## Syntax begin rescue retry end ## Example begin file = open("/unexistant_file") if file puts "File opened successfully" end rescue fname = "existant_file" retry end Here is the processing flow: * An exception occurs when opening. * Jump to rescue. `fname` is reassigned. * Jump back to the beginning of `begin` via `retry`. * This time the file opens successfully. * Continue the basic process. **Note:** If the renamed file doesn't exist, this example code will try infinitely. So use _retry_ cautiously in exception handling. 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 or raise "Error Message" or raise ExceptionType, "Error Message" or raise 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 interpret the exception before passing it on. The second form creates a new _RuntimeError_ exception, setting its message to the given string. The exception is then thrown to the calling stack. The third form creates an exception using the first parameter, then sets the related message to the second parameter. The fourth form is similar to the third form, you can add any additional conditional statements (like _unless_) to throw the exception. ## Example 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 of the above example is: I am before the raise. I am rescued. I am after the begin block. Another example demonstrating the use of _raise_: ## Example begin raise 'A test exception.' rescue Exception => e puts e.message puts e.backtrace.inspect end The output of the above example is: A test exception. ["main.rb:4"] Sometimes, you need to ensure some processing is done at the end of a code block, whether an exception is thrown or not. For example, you might open a file when entering the block, and when you exit the block, you need to ensure the file is closed. The _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 doesn't matter whether the block exits normally, throws and handles an exception, or terminates due to an uncaught exception, the _ensure_ block will always run. ## Syntax 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 of the above example is: A test exception. ["main.rb:4"] Ensuring execution If an _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 main body of the code does not throw an exception. ## Syntax begin rescue else ensure end ## Example begin 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 You can use the `$!` variable to capture the thrown error message. The exception mechanism of raise and rescue can abandon execution when an error occurs. Sometimes, you need to break out of some deeply nested structures during normal processing. This is where catch and throw come in handy. _catch_ defines a block that uses a given name (which can be a Symbol or String) as a label. The block executes normally until a throw is encountered. ## Syntax throw :lablename catch :lablename do ... end or throw :lablename condition catch :lablename do ... end ### Example In the following example, if the user enters '!' in response to any prompt, a throw is used to terminate the interaction with the user. ## Example 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 manual interaction, you can try it on your computer. The output of the above example is: 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 Exception class at the top. The next level consists of seven different types: * Interrupt * NoMemoryError * SignalException * ScriptError * StandardError * SystemExit Fatal is another exception in this hierarchy, but the Ruby interpreter only uses it internally. ScriptError and StandardError both have some subclasses, but we don't need to go into 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: ## Example 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: ## Example 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 a new instance of FileSaveError, due to a specific exception causing the data write to fail.
← Dom Obj MapRuby Dir Methods β†’