Every Rubyist knows about irb . The Interactive Ruby Shell is essentially a REPL (read-eval-print loop). Type in some expression, and the result gets returned immediately. So how does Pry fit in? Pry bills itself as a powerful alternative to the standard IRB shell. But it is much, much more than that.

In this article, I will guide you through some of the best features of Pry. Many of these features will alter your workflow in fundamental ways. The more you learn about Pry, the more you’ll wonder where it has been all this time.

Let’s get prying!

Installation

Getting Pry is simple:

% gem install pry pry-doc Fetching: pry-0.9.12.2.gem ( 100 % ) Successfully installed pry-0.9.12.2 Parsing documentation for pry-0.9.12.2 Installing ri documentation for pry-0.9.12.2 Fetching: pry-doc-0.4.6.gem ( 100 % ) Successfully installed pry-doc-0.4.6 Parsing documentation for pry-doc-0.4.6 Installing ri documentation for pry-doc-0.4.6 2 gems installed

Here, we install both pry and pry-doc . pry-doc provides MRI Core documentation and source code. We will need this in the later examples.

Now, just to make sure that everything works:

% pry -v Pry version 0.9 .12.2 on Ruby 2.0 .0

As of this writing, I am using the latest version of Pry. Pry works fine on both Ruby 1.9 and Ruby 2.0. Please note that I’m using Ruby MRI.

Part I: Let’s Break into Some Code

Personally, I find that the best way to learn Pry is to work through some examples. In this section, we look at the tools Pry gives us to display documentation and source code. In the process, we will also see how Pry uses shell navigation commands to navigate object state.

Let’s launch our Pry session:

% pry [ 1 ] pry ( main ) >

Showing Documentation with show-doc

Let’s say I need to recall the difference between Array#map and Array#map! . The show-doc command makes this a snap:

[ 2 ] pry ( main ) > show - doc Array From : array . c ( C Method ) : Owner : Array Visibility : public Signature : map ( ) Number of lines : 11 Invokes the given block once for each element of self . Creates a new array containing the values returned by the block . See also Enumerable If no block is given , an Enumerator is returned instead . a = [ "a" , "b" , "c" , "d" ] a . map { | x | x + "!" } a [ 3 ] pry ( main ) > show - doc Array From : array . c ( C Method ) : Owner : Array Visibility : public Signature : map ! ( ) Number of lines : 10 Invokes the given block once for each element of self , replacing the element with the value returned by the block . See also Enumerable If no block is given , an Enumerator is returned instead . a = [ "a" , "b" , "c" , "d" ] a . map ! { | x | x + "!" } a

Pry provides a very handy shortcut for show-doc – ? :

[ 3 ] pry ( main ) > ? Array

Navigating State with cd and ls

This is easily one of the coolest features of Pry. Let’s say I have an array:

[ 4 ] pry ( main ) > arr = [ 1 , 2 , 3 ] = > [ 1 , 2 , 3 ]

We can cd into an object, like so:

[ 5 ] pry ( main ) > cd arr [ 6 ] pry (

Notice how when we cd -ed into arr , the prompt changes to pry(# ):1> . This tells us that the current object is an Array instance, denoted by the # notation. “

Now, try ls -ing.

[ 6 ] pry ( Enumerable all ? each_entry find_all max minmax_by sort_by any ? each_slice flat_map max_by none ? chunk each_with_index grep member ? one ? collect_concat each_with_object group_by min partition detect entries inject min_by reduce each_cons find lazy minmax slice_before Array & count include ? reject slice * cycle index reject ! slice ! + delete insert repeated_combination sort - delete_at inspect repeated_permutation sort ! < < delete_if join replace sort_by ! <= > drop keep_if reverse take == drop_while last reverse ! take_while [ ] each length reverse_each to_a [ ] = each_index map rindex to_ary assoc empty ? map ! rotate to_s at eql ? pack rotate ! transpose bsearch fetch permutation sample uniq clear fill place select uniq ! collect find_index pop select ! unshift collect ! first pretty_print shelljoin values_at combination flatten pretty_print_cycle shift zip compact flatten ! product shuffle | compact ! frozen ? push shuffle ! concat hash rassoc size self . methods : __pry__ locals : _ __ _dir_ _ex_ _file_ _in_ _out_ _pry_

What else does ls do? Let’s ask Pry:

[ 14 ] ( pry ) main : 0 > ls - h Usage : ls [ - m | - M | - p | - pM ] [ - q | - v ] [ - c | - i ] [ Object ] ls [ - g ] [ - l ] ls shows you which methods , constants and variables are accessible to Pry . By default it shows you the local variables defined in the current shell , and any public methods or instance variables defined on the current object . The colours used are configurable using Pry . config . ls . * _color , and the separator is Pry . config . ls . separator . Pry . config . ls . ceiling is used to hide methods defined higher up in the inheritance chain , this is by default set to [ Object , Module , Class ] so that methods defined on all Objects are omitted . The - v flag can be used to ignore this setting and show all methods , while the - q can be used to set the ceiling much lower and show only methods defined on the object or its direct class . options : - m , -- methods Show public methods defined on the Object ( default ) - M , -- instance - methods Show methods defined in a Module or Class - p , -- ppp Show public , protected ( in yellow ) and private ( in green ) methods - q , -- quiet Show only methods defined on object . singleton_class and object . class - v , -- verbose Show methods and constants on all super - classes ( ignores Pry . config . ls . ceiling ) - g , -- globals Show global variables , including those builtin to Ruby ( in cyan ) - l , -- locals Show locals , including those provided by Pry ( in red ) - c , -- constants Show constants , highlighting classes ( in blue ) , and exceptions ( in purple ) - i , -- ivars Show instance variables ( in blue ) and class variables ( in bright blue ) - G , -- grep Filter output by regular expression - h , -- help Show this message .

Let’s go back to our arr object. Once we are in an object, we can call its methods directly, like so:

[ 7 ] pry ( = > 1 [ 8 ] pry ( = > 3 [ 9 ] pry ( = > [ 3 , 2 , 1 ]

Viewing the Source with show-method

Ever wondered how Array#map! is implemented? Recall that since we are already in an object, we can simply use show-source map! instead of show-source Array#map! .

[ 10 ] pry ( From : array . c ( C Method ) : Owner : Array Visibility : public Number of lines : 12 static VALUE rb_ary_collect_bang ( VALUE ary ) { long i ; RETURN_SIZED_ENUMERATOR ( ary , 0 , 0 , rb_ary_length ) ; rb_ary_modify ( ary ) ; for ( i = 0 ; i < RARRAY_LEN ( ary ) ; i ++ ) { rb_ary_store ( ary , i , rb_yield ( RARRAY_PTR ( ary ) [ i ] ) ) ; } return ary ; }

MRI is implemented in C, which explains the slightly cryptic listing. To get a Ruby code listing, you would have to switch to Rubinius, since most of its core libraries are built in Ruby. Studying the Rubinius code base with Pry is an excellent way to learn about Ruby implementation details in Ruby instead of C.

There are a few other ways of achieving the same result. But before that, let’s go up one level:

[ 11 ] pry ( [ 12 ] pry ( main ) >

The following are all equivalent:

[ 13 ] pry ( main ) > show - source Array [ 14 ] pry ( main ) > show - source arr . map !

Just like show-doc and ? , the equivalent for show-source is $ :

[ 15 ] pry ( main ) > $ Array [ 16 ] pry ( main ) > $ arr . map !

Part II: Diving deeper into Pry’s Debugging Facilities

So far, we’ve seen that Pry provides us with a very nice help system, along with ways to navigate and peek into the state of our objects. But Pry can do much, much more. In this section, we take a look at Pry’s debugging facilities.

Prerequisites

We need to tell Pry what our default editor should be. Create a file called .pryrc in your home directory. Since I love Vim:

Pry . config . editor = 'vim'

If you want to follow along, now would be a great time to grab the example file:

class Order def initialize @line_items = [ ] end def add_line_item ( line_item ) @line_items < < line_item end def total subtotals = @line_items . each { | li | li . quantity * li . price } subtotals . reduce ( : + ) end end class LineItem attr_reader :quantity , :price def initialize ( quantity , price ) @price = price @quantity = quantity end end order = Order . new order . add_line_item LineItem . new ( 2 , 3.00 ) order . add_line_item LineItem . new ( 4 , 1.00 ) puts order . total

Here is a very simple Order and LineItem class. An Order can add many LineItem s, each of which holds a quantity and price . An Order object can also calculate the total .

Run this program and watch it crash:

% ruby order.rb order.rb:12:in ` each': undefined method ` + ' for #<LineItem:0x007fb4e2013350 @price=3.0, @quantity=2> (NoMethodError) from order.rb:12:in ` reduce' from order.rb:12:in ` total' from order.rb:28:in ` < main > '

While this error might be trivial to solve, please bear with me – because here comes the fun part.

Setting Breakpoints with binding.pry

Since we know our program crashes on line 14, let’s put a breakpoint one line before that. Modify the totals method as follows:

def total subtotals = @line_items . each { | li | li . quantity * li . price } binding . pry subtotals . reduce ( : + ) end

This time, we run our program slightly differently. Note the -r pry flag:

% ruby -r pry order.rb From: /Users/rambo/Desktop/store/order.rb @ line 14 Order 12 : def total 13 : subtotals = @line_items.each { | li | li.quantity * li.price } = > 14 : binding.pry 15 : subtotals.reduce ( :+ ) 16 : end [ 1 ] pry (

What just happened? Firstly, your program did not crash. Instead, a Pry session was launched, and the execution of the program stops at where we placed binding.pry – our breakpoint. After that, we see pry(# )> , which means that we are in an Order instance. This is because binding.pry causes the Pry session to begin within the scope of our Order instance.

Let’s recall what ls does:

[ 1 ] pry ( Order instance variables : @line_items locals : _ __ _dir_ _ex_ _file_ _in_ _out_ _pry_ subtotals

Are our line_items properly populated?

[ 2 ] pry ( = > [

So far, so good. Take a closer look at the total method. Since binding.pry occurs after the subtotals variable, we can certainly access that:

[ 3 ] pry ( = > [

Aha! The result of subtotals is exactly the same as line_items . We should have used map instead of each ! Remember how we configured our editor previously? Now we will put that to good use.

Making edit s

Pry allows you to edit a method without ever leaving the session. We now know we must replace each with a map . So let’s do that:

[ 4 ] pry (

You will notice vim (or whatever editor your configured in .pryrc ) will launch, with the cursor at the first line of total . Make the necessary changes:

def total subtotals = @line_items . map { | li | li . quantity * li . price } binding . pry subtotals . reduce ( : + ) end

Exit the editor, and you would be brought back to the Pry session. Now, let’s see what does subtotals contain:

[ 1 ] pry ( = > [ 6.0 , 4.0 ]

Nice! When we exited from our editor, Pry automatically reloaded the file, and again stopped at binding.pry .

Running Shell Commands from Pry

Before removing binding.pry , we can check if the line after binding.pry works. Since I know the line number, I will go ahead and run the line:

[ 2 ] pry ( 10.0

Success! Now we can go ahead and remove binding.pry . But before we do, let’s see what changes we have made:

[ 3 ] pry (

Pry has the ability to run arbitrary shell commands. All you have to do is prefix the command with a . (dot), like what we did to git diff :

diff -- git a / order . rb b / order . rb index c05aa7d . .823 ccac 100644 -- - a / order . rb ++ + b / order . rb @@ - 10 , 7 + 10 , 9 @@ class Order end def total - subtotals = @line_items . each { | li | li . quantity * li . price } + subtotals = @line_items . map { | li | li . quantity * li . price } subtotals . reduce ( : + ) end end @@ - 26 , 4 + 28 , 4 @@ end order = Order . new order . add_line_item LineItem . new ( 2 , 3.00 ) order . add_line_item LineItem . new ( 4 , 1.00 ) puts order . total

View Stack Traces with wtf?

Let’s intentionally cause some trouble. Modify the code by adding another LineItem . To save us a bit of time, let us also put binding.pry before that line.

order = Order . new order . add_line_item LineItem . new ( 2 , 3.00 ) order . add_line_item LineItem . new ( 4 , 1.00 ) binding . pry order . add_line_item LineItem . new ( 1 / 0 , 100 ) puts order . total

Then we run the program:

% ruby - r pry order . rb From : / store / order . rb @ line 30 : 25 : end 26 : 27 : order = Order . new 28 : order . add_line_item LineItem . new ( 2 , 3.00 ) 29 : order . add_line_item LineItem . new ( 4 , 1.00 ) = > 30 : binding . pry 31 : order . add_line_item LineItem . new ( 1 / 0 , 1.00 ) 32 : puts order . total

Let’s go ahead and play line 31:

[ 1 ] pry (

As expected, the program crashes. To see a detailed stack trace, use the wtf? command:

[ 2 ] pry ( Exception : ZeroDivisionError : divided by 0 -- 0 : ( pry ) : 6 :in ` / ' 1 : ( pry ) : 6 :in `total' 3 : / usr / local / var / rbenv / versions / 2.0 .0 - p247 / lib / ruby / gems / 2.0 .0 / gems / pry - 0.9 .12 .2 / lib / pry / pry_instance . rb : 328 :in `evaluate_ruby' . . . 9 : / usr / local / var / rbenv / versions / 2.0 .0 - p247 / lib / ruby / gems / 2.0 .0 / gems / pry - 0.9 .12 .2 / lib / pry / pry_instance . rb : 231 :in `catch'

To see an even longer stack trace, simply append more ? . For example, wtf??? yields 30 lines. The Pry developers sure do have a sense of humour.

Analysing Stack Traces with cat --ex

Pry still has yet another trick up its sleeve. cat --ex directs you to the actual line which threw the exception:

[ 3 ] pry ( main ) > cat -- ex Exception : ZeroDivisionError : divided by 0 -- From : ( pry ) @ line 1 @ level : 0 of backtrace ( of 15 ) . = > 1 : order . add_line_item LineItem . new ( 1 / 0 , 1.00 )

cat --ex also takes in a number as an additional argument, which essentially walks you up the stack trace. This feature is very useful for debugging and tracing larger programs. (Anyone who has done any non-trivial Rails applications would definitely appreciate this feature.)

Wrapping Up

I hope this article has convinced you to give Pry a try (it has a nice ring to it, doesn’t it?). There is still quite a bit of material that was not covered. For example, we have not looked at how we could replace the Rails console with Pry. Also, we have not taken a look at Pry’s plugins, which add even more powerful features to an already impressive feature set.

So far, the only downside to Pry is the time it takes to launch a session. However, a slight delay is a small price to pay for all the power and flexibility Pry gives to you.

Do check out the Official Pry Wiki. The documentation is very comprehensive. There are also several links to where you can learn more about Pry, so I will not repeat it here.

Happy Prying!