Rubyists, It’s Time to PRY Yourself Off IRB!

Share this article

tOTeiZo

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#map

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#collect.

If no block is given, an Enumerator is returned instead.

   a = [ "a", "b", "c", "d" ]
   a.map { |x| x + "!" }   #=> ["a!", "b!", "c!", "d!"]
   a                       #=> ["a", "b", "c", "d"]

[3] pry(main)> show-doc Array#map!

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#collect.

If no block is given, an Enumerator is returned instead.

        a = [ "a", "b", "c", "d" ]
        a.map! {|x| x + "!" }
        a #=>  [ "a!", "b!", "c!", "d!" ]

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

[3] pry(main)> ? Array#map!

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(#<Array>):1>

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(#<Array>):1> ls
Enumerable#methods:
  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#methods:
  &            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(#<Array>):1> min
=> 1
[8] pry(#<Array>):1> max
=> 3
[9] pry(#<Array>):1> reverse
=> [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(#<Array>):1> show-source map!

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(#<Array>):1> cd ..
[12] pry(main)>

The following are all equivalent:

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

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

[15] pry(main)> $ Array#map!
[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 LineItems, 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 # <-- Add this
  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#total:

    12: def total
    13:   subtotals = @line_items.each { |li| li.quantity * li.price }
 => 14:   binding.pry
    15:     subtotals.reduce(:+)
    16: end

[1] pry(#<Order>)>

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>)> ls
Order#methods: add_line_item  line_items  total
instance variables: @line_items
locals: _  __  _dir_  _ex_  _file_  _in_  _out_  _pry_  subtotals

Are our line_items properly populated?

[2] pry(#<Order>)> line_items
=> [#<LineItem:0x007f80d25b62b0 @price=3.0, @quantity=2>,
 #<LineItem:0x007f80d25b6288 @price=1.0, @quantity=4>]

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(#<Order>)> subtotals
=> [#<LineItem:0x007f80d25b62b0 @price=3.0, @quantity=2>,
 #<LineItem:0x007f80d25b6288 @price=1.0, @quantity=4>]

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 edits

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(#<Order>)> edit total

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(#<Order>)> subtotals
=> [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(#<Order>)> play -l 15
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(#<Order>)> .git diff

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..823ccac 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(#<Order>)> play -l 31

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

[2] pry(#<Order>)> wtf?
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!

Frequently Asked Questions (FAQs) about Pry and IRB in Ruby

What are the key differences between Pry and IRB in Ruby?

Both Pry and IRB are interactive Ruby shells, but they have some key differences. IRB, which stands for Interactive Ruby, is the default REPL (Read-Eval-Print Loop) for Ruby. It’s a basic tool that allows you to execute Ruby code line by line. On the other hand, Pry is a more advanced REPL that offers a range of features not available in IRB, such as syntax highlighting, source code browsing, and live help system. It also allows you to step into your code with the use of binding.pry, which can be extremely helpful for debugging.

How can I use Pry for debugging in Ruby?

Pry is a powerful tool for debugging in Ruby. You can use the ‘binding.pry’ command to pause your code execution at any point and inspect the current scope. This allows you to check the values of variables, call methods, and even modify your code on the fly. To use it, simply insert ‘binding.pry’ into your code where you want to pause execution. When you run your code, it will stop at this point and open a Pry session, allowing you to interact with your code directly.

Can I use Pry and IRB interchangeably?

While both Pry and IRB are interactive Ruby shells, they are not completely interchangeable due to their different features. IRB is a simpler tool and may be sufficient for basic tasks, but Pry offers a range of advanced features that can be extremely useful for debugging and exploring your code. Therefore, it’s a good idea to familiarize yourself with both tools and use the one that best suits your needs for each task.

How can I install and set up Pry in my Ruby environment?

To install Pry, you can use the gem install command in your terminal: ‘gem install pry’. After installation, you can start a Pry session by simply typing ‘pry’ in your terminal. If you want to use Pry as your default Ruby shell, you can add the following line to your .irbrc file: ‘require ‘pry’; IRB = Pry’. This will start a Pry session whenever you type ‘irb’ in your terminal.

What are some advanced features of Pry that are not available in IRB?

Pry offers a range of advanced features that are not available in IRB. These include syntax highlighting, which makes your code easier to read, and a live help system, which allows you to access documentation directly from your Pry session. Pry also allows you to browse your source code and even modify it on the fly, which can be extremely useful for debugging. Additionally, Pry supports shell commands, which means you can use commands like ‘cd’ and ‘ls’ directly from your Pry session.

How can I use the live help system in Pry?

The live help system in Pry allows you to access documentation directly from your Pry session. To use it, simply type ‘help’ followed by the name of the method or class you want to learn about. For example, ‘help Array’ will show you the documentation for the Array class. This feature can be extremely useful for quickly looking up information without having to leave your Pry session.

Can I use Pry with Rails?

Yes, you can use Pry with Rails. In fact, Pry can be a powerful tool for debugging and exploring your Rails applications. To use Pry with Rails, you need to add the pry-rails gem to your Gemfile and run ‘bundle install’. Then, you can use ‘binding.pry’ in your Rails code to pause execution and open a Pry session, just like you would in a regular Ruby script.

How can I browse source code with Pry?

Pry allows you to browse your source code directly from your Pry session. To do this, you can use the ‘show-source’ command followed by the name of the method or class you want to view. For example, ‘show-source Array’ will show you the source code for the Array class. This feature can be extremely useful for understanding how different parts of your code work together.

Can I modify my code on the fly with Pry?

Yes, one of the powerful features of Pry is the ability to modify your code on the fly. This can be done using the ‘edit’ command, which opens the current file in your text editor. After making your changes and saving the file, Pry will automatically reload the file, allowing you to see the effects of your changes immediately.

How can I use shell commands in Pry?

Pry supports shell commands, which means you can use commands like ‘cd’ and ‘ls’ directly from your Pry session. To do this, simply type the command as you would in a regular shell. For example, ‘ls’ will list the files in the current directory, and ‘cd ..’ will move up one directory. This feature can be extremely useful for navigating your file system without having to leave your Pry session.

Benjamin Tan Wei HaoBenjamin Tan Wei Hao
View Author

Benjamin is a Software Engineer at EasyMile, Singapore where he spends most of his time wrangling data pipelines and automating all the things. He is the author of The Little Elixir and OTP Guidebook and Mastering Ruby Closures Book. Deathly afraid of being irrelevant, is always trying to catch up on his ever-growing reading list. He blogs, codes and tweets.

pry
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week