An exception is a special data structure that holds information about an exceptional condition happening in your application. Exceptions are signals sent when your program can't continue running because it doesn't know how to deal with a specific situation. In Ruby, we call sending such a signal raising an exception.
The quickest way to see an exception in action is to raise one yourself. By using the raise
method with a message, Ruby will create a RuntimeError
with the passed text as its description. It will also add a stack trace that points to where the exception was raised from in your code.
$ ruby raise.rb raise.rb:1:in `<main>': this is an exception (RuntimeError)
When running the example script above, you'll notice that, as promised, the message isn't printed after the exception is raised. That's because unhandled exceptions halt execution of your program by causing a crash. This is to make sure your application doesn't keep running after failing to complete one of its tasks.
Different types of exceptions
Internally, Ruby raises exceptions when things go wrong. For example, you'll get an exception when trying to devide a number by zero.
irb(main):001:0> 42/0 ZeroDivisionError: divided by 0 from (irb):1:in `/' from (irb):1 from /Users/jeff/.asdf/installs/ruby/2.3.0/bin/irb:11:in `<main>'
In this case, the exception's type is ZeroDivisionError
, instead of the RuntimeError
we saw before. There are dozens of exception types to form Ruby's exception hierarchy, and they're all raised in different situations.
RuntimeError
is a more generic exception class, but there are more specific examples. A NoMethodError
gets raised when trying to call a method that doesn't exist, and an ArgumentError
notes that the wrong arguments are passed to an existing method, for example.
Rescueing exceptions
Most applications require the ability to recover from exceptions. In a web application, an error can happen when a user submits faulty data in a form. This should result in an error message being displayed to the user instead of crashing the whole web server.
In a case like that, an exception might get raised, but it will get rescued by the web server before reaching the top of the call stack, which prevents the exception from crashing the application.
By wrapping the example from before in a begin/rescue block, the exception won't get raised, and it won't crash our app. Instead, the rescue-block gets executed, which prints a message explaining the exception was thwarted, in this case. Also, because this prevents our app from crashing, the last message will get printed.
This example specifically rescues from RuntimeError
s, so any other error won't get caught. Being specific about which errors are rescued is convenient, because it'll allow you to do different things when different errors occur. Also, it's important to make sure not to rescue too many exceptions.
For a more in-depth primer on exception handling, and some tips on how to properly rescue exceptions, check out our exceptions primer on Ruby Magic.
Have any questions about raising or catching exceptions in Ruby? Please don't hesitate to let us know at @AppSignal. Of course, we’d love to know how you liked this article, or if you have another subject you’d like to know more about.