What is an Exception?
Exceptions are an exceptional state in the course of the code. It’s a way for the programming language to let the programmer/user know that there is an unforseen process causing unexpected behavior.
In most programming language, when an exception is raised, the program stops and a message is sent back.
42 + 'answer'
# => TypeError (String can't be coerced into Integer)
A lot of programming language provides a hierarchy of built-in classes to simplify the exception handling.
In Ruby, the very first exception class is the Exception
class, which has several subclasses, many of which also have descendents of their own.
Exception to Handle
Exceptions can and for some should be handled. In Ruby, the descendents of the StandardError
class are errors which are safe to handle. They are caused by circumstances such as unexpected user input, faulty type conversions or dividing by zero.
Some errors, however, must not be handled; doing so would be dangerous. Handling them would result in masking critical errors and can make debugging more difficult than it already is.
This include importants errors such as NoMemoryError
, SyntaxError
and LoadError
.
How to Handle an Exceptional State
In Ruby, we use the begin
/rescue
block to handle errors. The block prevent your program from stopping if the specified exception is raised.
begin
# do something dangerous here
rescue TypeError
# do something if TypeError is raised
rescue ArgumentError
# do something else if ArgumentError is raised
end
When a issue is raised between begin
and rescue
, Ruby stops the execution of the code and goes straight to the rescue
block. In other words:
begin
array.fetch(index) # The index is out of bonds
puts "This code won't execute if out of bonds"
rescue IndexError
puts "Out of bonds!"
end
In the code above, te puts
method call behind the array.fetch
line will not execute if index
is out of bonds; Ruby goes straight to rescue
.
It is also possible to specify multiple exception on the same line in order to take the same action for more than one type of exception:
begin
# do something dangerous here
rescue ZeroDivisionError, TypeError
# do something if ZeroDivisionError or TypeError are raised
end
Useful Clauses in Ruby
There are several useful clause to use along the begin
/rescue
block. Let’s see some of them.
begin
# do something dangerous here
rescue
# handle the inevitable exception
ensure
# do this every time, exception or not
end
ensure
is run whether an exception was raised or not. It’s useful to close file or connection with database, for example.
Another example is retry
: it lets you retry running your code after it raised an exception:
RETRY_LIMIT = 5
begin
attempts = attempts || 0
# do something dangerous
rescue
attempts += 1
retry if attempts < RETRY_LIMIT
end
This is useful when dealing with network connection and remote connection, where the line can sometime be busy.
Manually Raising Exceptions
It is possible to manually raises exception. In Ruby, it is done with the… raise
method!
raise TypeError.new("Task failed successfully.")
Exceptions raised manually can, or course, be handled in the same manner as exceptions Ruby raises automatically.
Crate Custom Exceptions
Exceptions being classes, we can create our own custom exception classes, if we so wish.
class ValidateSomethingError < StandardError
# validate something one way or anotehr
end