pry to the rescue

Conrad Irwin — August 2012

Introducing: pry-rescue: super-fast, painless, debugging for the (ruby) masses.

What does it do?

Whenever an unhandled exception happens in your program, pry-rescue opens an interactive pry shell right at the point it was raised. Instead of glowering at a stack-trace spewed out by a dying process, you’ll be engaging with your program as though it were still alive!

The pry console gives you access to the method that raised the exception, you can use it to inspect the values of variables (no more print statements!), the source code of methods (no more flapping around with a text editor!), and even move up and down the call stack (like a real debugger!).

Because the shell opens as though the exception were just about to be raised, you don’t even need to re-run your program from the beginning in order to start debugging. It’s optimized for the “exceptions-happen” workflow that you need when developing code.

Ok, sounds good, how do I use this?

It’s easy: gem install pry-rescue pry-stack_explorer, and then run your program using rescue foo.rb instead of ruby foo.rb. If rescue doesn’t float your boat, then just wrap chunks of your program in Pry::rescue{ }. Any exceptions that are unhandled within that block will be rescued by pry on your behalf. (rack middleware coming soon!)

Whenever pry opens because of an exception you have two choices: hit <ctrl-d> to let the exception bubble on its way, or do some debugging! If you choose the latter path, then you can use the full power of pry to fix the problem; and then try-again to verify the fix worked.

Uh… do you have an example?

Sure! Let’s imagine that I wrote some code that looks like this:

def find_capitalized(a) do |name|
     name.chars.first == name.chars.first.upcase

Now I run the code: rescue rescue.rb

From: rescue.rb @ line 2 Object#find_capitalized:

    2: def find_capitalized(a)
    3: do |name|
 => 4:     name.chars.first == name.chars.first.upcase
    5:   end
    6: end

NoMethodError: undefined method `chars' for :direction:Symbol
from rescue.rb:4:in `block in find_capitalized'
[1] pry(main)>

Well, it’s gone wrong. But at least I can see why. Let’s move up the stack and see if we can find which code is calling this method with symbols:

[1] pry(main)> up
From: rescue.rb @ line 8 Object#extract_people:

     8: def extract_people(opts)
 =>  9:   name_keys = find_capitalized(opts.keys)
    11:   name_keys.each_with_object({}) do |name, o|
    12:     o[name] = opts.delete name
    13:   end
    14: end

[2] pry(main)> opts
=> {"Arthur"=>"Dent", :direction=>:left}

Ok, that seems odd, but fair enough, let’s see if there’s a better way of implementing find_capitalized:

[3] pry(main)> down
[4] pry(main)> name.first
NoMethodError: undefined method `first' for :direction
from (pry):9:in `block in find_capitalized'
[5] pry(main)> name.capitalize
=> :Direction

Got it. Let's `edit-method` to fix the code,  and `try-again` to verify the fix worked:

[6] pry(main)> edit-method
[7] pry(main)> whereami
From: rescue.rb @ line 2 Object#find_capitalized:

    2: def find_capitalized(a)
 => 3: do |name|
    4:     name.capitalize == name
    5:   end
    6: end

[8] pry(main)> try-again
Arthur Dent moves left

What else do I need to know?

Well, there are a myriad of pry commands that are useful while debugging: `wtf?` and
`cat --ex` can be used to examine stack traces and the associated code; `cd` and `ls`
can be used to closely examine objects; `$` and `edit-method` can be used to view and edit
source code. The best thing to do is just type `help` once you've installed pry.

The other useful `pry-rescue` command is `cd-cause`. It lets you rewind back the the
previously raised exception. So, if you've rescued one exception, and then raised another
(it happens…) you can jump back to the original cause of the problem.

Apart from that, the only thing you might want to know is that you can use
`Pry::rescue{ }` with exceptions that you handle yourself. Just call `Pry::rescued(e)` in
your rescue block.

So, `gem install pry-rescue` now. If you find problems, please
let me know on [github](