Sometimes the neither the system nor the language detect an error, but you do. Perhaps the user input someone 18 years old for Medicare. Linux doesn't know that's wrong. Ruby doesn't know that's wrong. But you do.
You can raise a generic exception (or the current exception if there is one) like this:
Exceptions are handled by blocks. The risky code goes into the first chunk of the block. If an exception is encountered, the program will jump to the end of that chunk and look for an appropriate action. In the above example, it will first look at
Otherwise, it will look further. The next rescue is for
The
An exception is raised using the
You can put a
Also in that chunk, note that the
Stack Trace
To see the stack trace, use the
Exceptions that do not inherit from StandardError
The superclass for all exceptions is
However, there may be times you want to recover from other exceptions. A particular example I came across is a
To catch all exceptions use:
The rescue statement modifier
Rescue can be used in a way analogous to if and unless. For example:
This statement will run the method test, and if it encounters an error (specifically StandardError or subclasses) it will invoke do_stuff. You can use this to assign a default value if a method fails, like this:
The rescue stament returns the value "Default Value". You could write it with brackets, which might help make sense of it:
You can concatenate statement with semi-colons, but cannot use blocks.
Catch and Throw
The catch and throw facility in Ruby is not really exception handling at all, though it does have similarities. As it shares keywords with Java exception handling, it seems to get lumped into any discussion on exceptions. This page is no different.
Calling a
You can use the throw to return a value to catch (which will be nil otherwise).
You can raise a generic exception (or the current exception if there is one) like this:
raise if age < 65
begin age = 60 puts 'Here 1' raise MyError.new "Text" unless age < 65 puts 'Here 2' rescue MyError puts 'Here 3 - MyError encountered' retry // here if you want to run that code which is produced error use retry rescue StandardError puts "Here 4 - Other error encountered (#{$!.inspect})" + caller.inspect raise else puts 'Here 5 - No errors' ensure puts 'Here 6 - Always done' end
Exceptions are handled by blocks. The risky code goes into the first chunk of the block. If an exception is encountered, the program will jump to the end of that chunk and look for an appropriate action. In the above example, it will first look at
rescue MyError
. If the exception is of the MyError
class - or a sub-class - then the program will not run the code folling the rescue. Note that you can list multiple exception classes, separated by commas.Otherwise, it will look further. The next rescue is for
StandardError
, so this will catch all errors of the StandardError
type or its sub-classes - except for MyClass
, as that would have been caught earlier. All rescuable exceptions must inherit from StandardError
(but see later), so this is set up to catch all exceptions, but that need not be the case; uncaught exceptions will be passed on up the stack to whatever else might handle them.The
else
chunk will get performed after the initial chunk has completed successfully (i.e., without raising an exception). Finally, the ensure
chunk gets performed whatever the outcome.An exception is raised using the
raise
keyword. Exceptions are just objects, so are instantiated just like any other object. They typically take a string parameter, which you can use to give a descriptive message. Alternatively, you use this form:raise MyError.new "Test" raise MyError, "Text", caller raise MyError, "Text" raise "Text" # For the default RuntimeError
You can put a
retry
in a rescue chunk, as in the example above. The program will jump back to the start of the block, and start again. In the second rescue
chunk, there is a raise
on its own. This will pass the exception ouside of the block, to be handled by some higher up error handling system.Also in that chunk, note that the
$!
code is the exception. If you prefer, you can set your own name for this.rescue StandardError => err puts "Here 4 - Other error encountered (#{err.inspect})" + caller.inspect raiseHere is the output from the first example:
Here 1 Here 3 - MyError encountered Here 1 Here 3 - MyError encountered Here 1 Here 3 - MyError encountered Here 1 Here 3 - MyError encountered Here 1 Here 3 - MyError encountered Here 1 Here 2 Here 5 - No errors Here 6 - Always done
Stack Trace
To see the stack trace, use the
backtrace
method. This returns an array of strings. To just see the top dozen entries, you could use this:$!.backtrace[0..12] * "\n"
Exceptions that do not inherit from StandardError
The superclass for all exceptions is
Exception
. The theory is that all exceptions that a program should be expected to recover from are either StandardError
or a subclass of it, which is why I described them as "rescuable exceptions " earlier. If no exception is specified, rescue will default to StandardError
.However, there may be times you want to recover from other exceptions. A particular example I came across is a
SyntaxError
thrown by ERB. The offending syntax was in a data file, not my application; my application should have caught the error, and reported it back to the user. This is entirely different to a syntax error in my code, and in my opinion ERB should throw its own exceptions that inherit fromStandardError
. Time-out errors are another example, as discussed here.To catch all exceptions use:
rescue Exception
The rescue statement modifier
Rescue can be used in a way analogous to if and unless. For example:
test rescue do_stuff
This statement will run the method test, and if it encounters an error (specifically StandardError or subclasses) it will invoke do_stuff. You can use this to assign a default value if a method fails, like this:
s = test rescue "Default value"
The rescue stament returns the value "Default Value". You could write it with brackets, which might help make sense of it:
s = (test rescue "Default value")
You can concatenate statement with semi-colons, but cannot use blocks.
s = test rescue do_stuff; "Default value"
Catch and Throw
The catch and throw facility in Ruby is not really exception handling at all, though it does have similarities. As it shares keywords with Java exception handling, it seems to get lumped into any discussion on exceptions. This page is no different.
Calling a
throw
will interrupt the program flow, causing it to jump to the named catch
. Use this format to set up a catch block. Any timethrow :my_label
is called within the block (including within methods called from the block).catch :my_label do do_stuff throw :my_label do_not_do this_stuff end
You can use the throw to return a value to catch (which will be nil otherwise).
value = catch :my_label do do_stuff throw :my_label, "My result" do_not_do this_stuff end