It's often useful to be able to get the most recent exception, even if your code doesn't control the lifecycle of that exception. Imagine that you want to add basic crash detection to your application. You'd like to log extra info about any crash that happens as a result of an uncaught exception.

The first step is to add a handler that's run whenever your application exits. It's super easy to do this via the Ruby kernel's at_exit method.

at_exit
  puts "the app exited"
end

But how can we know if the exit callback was invoked as a result of an exception? Well, Ruby provides the cryptically named $! global variable. It contains the most recently raised exception that has occurred somewhere in the current call stack.

It's trivial to use $! to detect if the program is being exited due to an exception. It looks something like this:

at_exit do
 save_error_to_log($!) if $!         
end

The limitations of $!

Unfortunately, the $! method only works if the exception occurred somewhere in the current call stack. If you rescue an exception, then try to access $! outside of the rescue clause, you'll get nil.

begin        
 raise "x"       
rescue       
 puts $! # => RuntimeError           
end

puts $! # => nil         

This means that $! is pretty useless inside of a shell like IRB. Often in IRB, I'll run a method and get an exception. Sometimes I'd like to get ahold of that exception object. But $! doesn't work for this.

irb(main):001:0> 1/0
ZeroDivisionError: divided by 0
    from (irb):1:in `/'
irb(main):002:0> $!
=> nil

Working around $! with PRY

PRY gets around the limitations of $! by adding its own local variable, _ex_. This variable contains the most recent uncaught exception.

[1] pry(main)> raise "hi"        
RuntimeError: hi         
from (pry):1:in `__pry__'        
[2] pry(main)> _ex_      
=> #<RuntimeError: hi>

The reason that PRY is able to do this is because there are not really any uncaught exceptions inside of PRY or IRB. The shell itself catches the exceptions and displays them as nicely-formatted error messages.

I've copied the relevant bits of the PRY source below. You can see that the code that evaluates your commands is wrapped inside of a begin/rescue/end block. When a rescuable exception occurs, PRY saves the exception to self.last_exception and it later gets assigned to _ex_.

# Excerpted from the PRY source at https://github.com/pry/pry/blob/623306966bfa86890ac182bc8375ec9699abe90d/lib/pry/pry_instance.rb#L273

begin
  if !process_command_safely(line)
    @eval_string << "#{line.chomp}\n" if !line.empty? || !@eval_string.empty?
  end
rescue RescuableException => e
  self.last_exception = e
  result = e

  Pry.critical_section do
    show_result(result)
  end
  return
end

Require English

Perhaps you find variable names like $! a little hard on the eyes? Fortunately, Ruby includes a module called "English" which provides english-language versions of many global variables which otherwise look like robot cusswords.

The synonym for $! is $ERROR_INFO. You can use it wherever you'd normally use $!.

require "English"

begin        
 raise "x"       
rescue       
 puts $ERROR_INFO # => RuntimeError          
end

And although most of the other english equivalents have nothing whatsoever to do with the topic of this blog post, I'm including them for kicks. English variables are on the left. The originals are on the right.

$ERROR_INFO $!
$ERROR_POSITION $@
$FS $;
$FIELD_SEPARATOR $;
$OFS $,
$OUTPUT_FIELD_SEPARATOR $,
$RS $/
$INPUT_RECORD_SEPARATOR $/
$ORS $\
$OUTPUT_RECORD_SEPARATOR $\
$INPUT_LINE_NUMBER $.
$NR $.
$LAST_READ_LINE $_
$DEFAULT_OUTPUT $>
$DEFAULT_INPUT $<
$PID $$
$PROCESS_ID $$
$CHILD_STATUS $?
$LAST_MATCH_INFO $~
$IGNORECASE $=
$ARGV $*
$MATCH $&
$PREMATCH $`
$POSTMATCH $‘
$LAST_PAREN_MATCH $+