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!
Key Takeaways
- Pry offers a richer set of features compared to IRB, including advanced navigation tools, documentation access, and source code browsing, making it a superior choice for Rubyists looking to enhance their coding and debugging efficiency.
- Installation of Pry and its documentation is straightforward via RubyGems, ensuring Rubyists can quickly set up and dive into its extensive features.
- Pry’s ability to navigate object states using commands like `cd` and `ls` and to view and edit source code directly within the REPL provides a powerful environment for dynamic code interaction and debugging.
- The debugging capabilities of Pry, especially using `binding.pry` for setting breakpoints, allow developers to inspect and modify code execution in real-time, offering a significant advantage over traditional debugging methods.
- Pry’s integration with shell commands and the ability to run these directly from the session enhances its utility, making it a versatile tool that goes beyond simple code execution to facilitate a comprehensive development workflow.
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(#
. 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 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 # <-- 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 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(#<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 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.