Ruby exceptions and error handling is not the most remarkable feature of the Ruby language. Infact, the way Ruby deals with exceptions is strikingly similar to other languages (such as Java etc.). But, I think all the stuff I’ve been writing about Ruby lately has taken on a life of it’s own for me :), so I am going to quickly go over this topic if only for completeness sakes.
Getting Ruby to raise an exception is easy :). Ruby, like many other languages has a hierarchy of exception classes (that all inherit from the class Exception), some of these are familiar and fairly easy to produce, for example ZeroDivisionError or NoMethodError. Here is some code which will do just that:
blah = Object.new blah.hello
Of course you don’t have to wait for Ruby to raise exceptions for you, you can do so explicitly in your code with the raise keyword (it is actually a method). Lets write a method where we explicitly raise an exception if the argument we pass in is false:
def i_must_have_truth(value) raise TypeError, 'You must give me truth' if value == false end i_must_have_truth false
D:\ruby-projects\scrap\lib\scrap.rb:15:in `i_must_have_truth': You must give me truth (TypeError) from D:\ruby-projects\scrap\lib\scrap.rb:18
As you can see we are able to raise a specific exception and pass it a message which then gets printed out to the console. There is also a handy shortcut, if you use raise without giving it a specific exceptions (or even without giving it an error message to display), Ruby will automatically raise a RuntimeException for you which is pretty handy:
def i_must_have_truth(value) raise "Hello" end i_must_have_truth false
D:\ruby-projects\scrap\lib\scrap.rb:15:in `i_must_have_truth': Hello (RuntimeError) from D:\ruby-projects\scrap\lib\scrap.rb:18
So far so good, but life would be pretty tough if we couldn’t handle the exceptions that get thrown in any way, This is where the rescue clause comes in. If we wrap some code in a begin .. end block and put a rescue clause in the middle of that, control will go to the rescue clause if any exception is thrown by the code. Let us demonstrate:
begin 1/0 p 'I should never get executed' rescue p 'I am rescuing an exception and can do what I want!' end
This produces the following output:
"I am rescuing an exception and can do what I want!"
As you can see the first string does not get printed since the division by zero will throw an exception and control will pass to the rescue clause, which will print out the second string for us.
Using rescue by itself will allow you to rescue all exceptions that get thrown by the code, but sometimes you might want to only capture specific ones and let other code handle all the others. The rescue clause allows you to do this as well:
i=0 while i<=10 begin if i ==0 1/0 end raise "random runtime exception" p 'I should never get executed' rescue ZeroDivisionError p 'I am rescuing only ZeroDivisionErrors!' i+=1 end end
which gives us:
"I am rescuing only ZeroDivisionErrors!" D:\ruby-projects\scrap\lib\scrap.rb:33: random runtime exception (RuntimeError)
As you can tell, we rescued the first exceptions since it was a division by zero, but the second exception does not get rescued and so the program exits with an error. However, we sometimes want to execute some code regardless of whether an exception was thrown or not (i.e. we may want to do some cleanup). Java has the finally keyword for this, Ruby has ensure. We can put an ensure clause within out begin .. end block. The code inside this clause gets executed regardless of whether the code throws an exception. For example, if we are reading from a file, we want to close the file handle no matter if an exception is thrown or not, we can do the following:
file = nil begin file = File.open("blah.txt") raise rescue p 'I rescue all exception and raise new ones' ensure file.close p 'just closed the file' end
as we expect the output is:
"I rescue all exception and raise new ones" "just closed the file"
Even though an exception was thrown and rescued, the code in the ensure clause executes regardless.
Rescuing Exceptions Inside Methods
If we are inside a method and we want to rescue some exceptions, we don't actually need a begin .. end block since the method definition itself will act in that capacity. So, we can do something like this:def some_method p 'Hello method' raise p 'Bye method' rescue p 'Rescuing exceptions' end some_method
which print out:"Hello method" "Rescuing exceptions"
We have rescued an exceptions without having a begin .. end block.
You are not limited to knowing just the type of the exception you're rescuing, you can get more information at your disposal. When you rescue an exception you can get a hold of the actual exception object that was thrown and then query this object for various details. Here is how:
begin raise ZeroDivisionError, "Hello I am a random zero division error" rescue ZeroDivisionError => e p e.message p e.backtrace end
If we execute the above code we will print out the message as well as the stack trace of the exception we rescued which are provided by the exception object itself. You can also do the same thing with a general rescue clause:
begin raise "Hello I am a random runtime error" rescue => e p e.message p e.backtrace end
Finally if you have rescued an exception, but don't want to handle it yourself (for whatever strange reason), calling raise within a rescue block with no parameters will allow you to re-raise the original exception that you have rescued rather than the normal behavior (i.e. raising a general runtime exception).
Creating Your Own Exceptions
Creating your own exceptions in Ruby is extremely simple, all you need to do is create a class that inherits from Exception or one of it's descendants and you're good to go:
class MyCrazyException < Exception end raise MyCrazyException, "I am a crazy new exception"
this prints out:
D:\ruby-projects\scrap\lib\scrap.rb:78: I am a crazy new exception (MyCrazyException)
As you can see Ruby is saying that it has raised our new exception rather than one of it's regular ones. As usual, this allows you to define different types of exceptions for various error conditions in your code which also allows you to rescue them by name. You may or may not want to do this, you can easily get away with just using runtime exceptions but it is up to you.Update: Several people have pointed out, in the comments that inheriting from Exception directly is a big no-no when creating custom exceptions. You should instead inherit from StandardError. This is because exceptions that inherit from StandardError deal with application level errors, while if you inherit Exception directly you risk catching errors to do with the environment. Also the convention is to end your exceptions with the word Error rather than exceptions e.g.:
class MyCrazyError < StandardError end
The Ruby Exception Hierarchy
If you're curious about the kinds of exceptions that Ruby has predefined for you, here is a list:
Exception NoMemoryError ScriptError LoadError NotImplementedError SyntaxError SignalException Interrupt StandardError ArgumentError IOError EOFError IndexError LocalJumpError NameError NoMethodError RangeError FloatDomainError RegexpError RuntimeError SecurityError SystemCallError SystemStackError ThreadError TypeError ZeroDivisionError SystemExit fatal
You don't have to take my word for it, you can derive it yourself :). The only thing that is missing from that list is the Errno family of exceptions. These are a whole set of exceptions that relate to file I/O and live in the Errno namespace. You can see one of these in action if you try to open a file that doesn't exist, e.g.:
which gives you an Errno style exception:
D:\ruby-projects\scrap\lib\scrap.rb:80:in `open': No such file or directory - File not found - (Errno::ENOENT) from D:\ruby-projects\scrap\lib\scrap.rb:80
That's all, nothing too fancy, just the kinds of basics that everybody needs to know. You're now a Ruby exception guru, go forth and use your new Ruby error handling powers for good instead of evil :).
Image by valentin.d