5.1 Tidying Things Up

Capture and control flow

Exceptions are simply, just packaged objects that contain error information. These are typically meant for more exceptional cases that require halting the current program flow to handle abnormal conditions. This is different from else clauses, error codes or state variables which are more about guiding the control flow.

They also allow you to clearly segregate the expected processing of your workflow from the anticipated exceptional cases in your code

Example Code:

Output Window

This is exactly what we want. It does a few important things.

Our code is neatly segregated from the part where the exception is handled. The exception is handled after the rescue clause and everything before that in the begin block is code that has problems that we can anticipate.

In this next example, we call the backtrace method on the error variable, which is an instance of the SyntaxError class. This variable allows us to go through the current call stack of your program.

Example Code:

Output Window

Another important thing it allows us to do is halt execution. Notice how it exits the block after the second eval line raises an exception. Nothing else is executed after it.

Example Code:

Output Window

This is one of the differences between raising error codes and raising exceptions. Exceptions preempt the usual workflow, error codes are more passive, and explicitly need to be handled later on in your program.

Here's what an error code handling flow might look like, handling zen? that returns 200 in the if block sometime after.
Example Code:

Output Window

Error codes are also specific to the domain of your problem. Exceptions, on the other hand, by default, are more tightly coupled with the Ruby API. It is, however, possible to create custom exceptions, other than the ones already defined by the Exception class.

Propagation

Exceptions always bubble up towards the relevant exception handler. Imagine as if you were trying to chase a ghost in a haunted house running across a chain of rooms. We go outwards in terms of how our blocks are scoped, we're technically going deeper into our call stack.

Example Code:

Output Window

Notice how the division by zero exception propagates outwards from the 10.times block → to the zen method → to the rescue ZeroDivisionError clause outside the method. The backtrace is then printed after the exception is rescued, and any further execution is stopped.

What happens when you don't handle the exception anywhere?
Example Code:

Output Window

Just like before, the line where answer is being assigned, an exception is raised. This exception propagates further up the call stack. The caller of the 10.times block is zen for which the caller is the begin block. At this point, the exception is not explicitly rescued anywhere, hence it is just printed onto the STDERR and the program exits.

Hierarchy and the Exception class

All the error objects that we've been using come from the Exception class. These objects are typically created by raise or by simply calling new on the exception object.

The Exception is the master class that subclasses a bunch of more granular exceptions.
Example Code:

Output Window

The larger part of the commonly recoverable exceptions are sub-classed by the StandardError class. The others are more low, virtual-machine level errors that are less important in a regular course of the program and are generally not captured.

This is the StandardError class hierarchy:

   ArgumentError
   IOError
     EOFError
   IndexError
   LocalJumpError
   NameError
     NoMethodError
   RangeError
     FloatDomainError
   RegexpError
   RuntimeError
   SecurityError
   SystemCallError
   SystemStackError
   ThreadError
   TypeError
   ZeroDivisionError

For more granularity in your exceptions, you can rescue each of these individually. Rescuing StandardError takes care of all the sub-classed exceptions as well. In this example, we rescue ZeroDivisionError by rescuing StandardError.

Example Code:

Output Window

Therefore, to create a custom Exception, you can subclass the Exception class. More specifically, the StandardError class.
Example Code:

Output Window

It is generally a good practice to end your custom exception name with Error.

backtrace method on the Exception class returns an array of the current state of the call stack. Starting from the first element, it gives you strings of errors where the exception originated from, till it reaches the end of the call stack.

message returns a string that is specified when you create or raise the exception object. It returns the name of the exception if no message is specified.

Example Code:

Output Window

Raise a custom exception handler KasayaError in the robe method if the argument type is not a String "Kasaya". It should return "Dharmaguptaka's Kasaya Robe" otherwise.

Output Window

Congratulations, guest!


% of the book completed

or

This lesson is Copyright © 2011-2024 by Jasim A Basheer