Pry-ing open Ruby
Pry is an excellent Ruby REPL and debugger and is my go-to tool for both purposes. In this article, I'll go through how to get Pry going in a Ruby project and hit the highlight reel of its most useful methods and features.
Installing Pry for REPL and Debugging
Here are the gems we need. The pry gem supplies the REPL and the pry-byebug plug-in adds required debugging commands. To see documentation on code in the REPL, you'll need pry-doc. If you want nice REPL text highlighting, use pry-coolline.
Below are two methods for installing Ruby Gems. For Gems I use globally such as Pry, I generally install from the command line, but use whatever method you prefer. If you only want the REPL, skip pry-byebug.
Installing pry from the command-line
lee@lees-rig:~$gem install pry pry-doc pry-coolline pry-debug
Installing pry from a Gemfile.
Add the following lines of code to your Gemfile. These are the most current versions as of the date of this article, so if you need to get the most updated version of the gems go to pry, pry-byebug, pry-coolline, and pry-doc.
gem 'pry', '~> 0.11.3' # the pry repl iself (required)
gem 'pry-coolline', '~> 0.2.5' # add text highlighting. (neat but optional)
gem 'pry-doc', '~> 0.13.4' # supplies documentation in pry (recommended)
gem 'pry-byebug', '~> 3.4' # adds debugging commands (needed for debugging)
Pry as a REPL
I use REPLs to test code snippets while I work, and Pry is the best REPL for Ruby (in my opinion). IRB is a useable REPL, but as simple as it is to install pry, why not use it? Let's fire up Pry from the command line. Open your terminal of choice, type pry and press enter.
lee@lees-rig:~$pry
REPL commands
Pry has a myriad of useful commands, but here is a short list of the ones I find most useful:
_ — underscore returns the last output. This is quite useful if you want to augment or extend your previous return value.
[1] pry(main)> hi = "hi"
=> "hi"
[2] pry(main)> hi_ally = _ + " Ally"
=> "hi Ally"
_out_ — the array of all outputs.
[3] pry(main)> _out_.each { |line| puts line }
hi
hi Ally
_in_ — the array of all inputs.
[4] pry(main)> _in_[0]
=> "hi = \"hi\"\n"
⇅ up/down arrow — cycles through the array of session inputs. A single press of the up arrow gives your last entry. This is quite useful for typos.
tab — auto-completes your entry if possible. If there is more than one possibility, pressing tab twice lists the all the options.
[4] pry(main)> hi
hi hi_ally hist history
ls — lists the variables and methods within the current scope.
[18] pry(main)> ls hi_ally
Comparable#methods: < <= > >= between? clamp
String#methods:
% casecmp delete_suffix! gsub ord slice to_s
... all the string methods
capitalize! delete_suffix grapheme_clusters oct size to_r
self.methods: __pry__
? <topic of interest> — returns the documentation. To look up methods by class, use ? Array#each
gives the same result as ? [1,2,3].each
.
[17] pry(main)> ? String#upcase
From: string.c (C Method):
Owner: String
Visibility: public
Signature: upcase(*arg1)
Number of lines: 6
Returns a copy of str with all lowercase letters replaced with their
uppercase counterparts.
See String#downcase for meaning of options and use with different encodings.
"hEllO".upcase #=> "HELLO"
$ <topic of interest> — returns the code for the topic. Works identically to ?.
cd <name of object> — cd into an object to make it the working context.
cd .. — exits the object or context. Cd works much as it does in the terminal.
[10] pry(main)> cd hi_ally
[11] pry("hi Ally"):1> ls
Comparable#methods: < <= > >= between? clamp
String#methods:
% casecmp delete_suffix! gsub ord slice to_s
self.methods: __pry__
locals: _ __ _dir_ _ex_ _file_ _in_ _out_ _pry_
[12] pry("hi Ally"):1> cd ..
[13] pry(main)>
exit — does the same thing as cd .. except that it will exit Pry in main.
.<shell command> — executes a shell command instead of a Pry command.
[12] pry(main)> .ls
bin Gemfile Gemfile.lock README.md
crtl+r — pressing ctrl+r opens a search of the input history. Pressing ctrl+r again while in the search toggles through the possible matches.
(reverse-i-search)`hi_ally': ally = _ + " Ally"
Pry as a Debugger
Ok, so here is the really handy stuff. Cracking into your code while it's running is a rapid way to solve tricky issues. Pry freezes the code at specific points and lets you query variables and more via its REPL.
Debugger commands
Here are the most useful debugging commands:
step — executes the current line, and stops at the first possible occasion. Will step inside of called methods. Also, stops at all binding.pry
. Note: will step inside of binding.pry
itself if it is the next line of code.
next — continues execution until the next line in the current function is reached, or it returns. Does not step inside called methods. Also, stops at all binding.pry
.
continue — runs code until the next binding.pry
!!! — leaves the debugger session. Very useful if you get stuck long loop.
Okay, Let's do a little debugging. Here's some sad, broken code to work with. Copy the code into a file called broken.rb to follow along.
def shuffle_if(arr)
if arr.length > 10
arr.shuffle! # shuffles in place
end
end
arr = (1..10).to_a # creates a basic array
arr = shuffle_if(arr) # re-assigns arr to it's shuffled self?
puts arr.last
running broken.rb we get the following
lee@lees-rig:~$ruby broken.rb
Traceback (most recent call last):
broken.rb:9:in `<main>': undefined method `last' for nil:NilClass (NoMethodError)
So you and I both know what's wrong here, but let's assume we didn't. From the error, we can glean that the value of arr at the time we call arr.last
is no longer an array and that arr == nil
, but let's drop in a binding.pry
statement to set a breakpoint. Also, let's require pry so we can use it.
require 'pry'
def shuffle_if(arr)
if arr.length > 10
arr.shuffle! # shuffles in place
end
end
arr = (1..10).to_a # creates a basic array
binding.pry
arr = shuffle_if(arr) # re-assigns arr to it's shuffled self?
puts arr.last
Rerun the file using ruby broken.rb
. This time it opens the Pry debugger. Let's return arr so we can take a look at its value.
$ ruby broken_2.rb
From: /home/broken.rb @ line 13 :
8: end
9: end
10:
11: arr = (1..10).to_a # creates a basic array
12: binding.pry
=> 13: arr = shuffle_if(arr) # re-assigns array to it's shuffled self?
14: puts arr.last
[1] pry(main)> arr
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
At our current position at the binding.pry
, I can't indentify any issues with arr. Let's step forward.
[3] pry(main)> step
From: /home/broken.rb @ line 4 Object#shuffle_if:
3: def shuffle_if(arr)
=> 4: if arr.length > 10
5: arr.shuffle! # shuffles in place
6: end
9: end
[3] pry(main)> arr
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[4] pry(main)> arr.length
=> 10
At this point in the code, we can see what's happening. Arr is still fine but given its length of 10 the if statement won't execute. Since Ruby methods return nil if not given a return value, we'll be assigning arr a value of nil. Let's step one more time to make sure.
[1] pry(main)> step
From: /home/broken.rb @ line 12 :
7: end
8:
9: arr = (1..10).to_a # creates a basic array
10: binding.pry
11: arr = shuffle_if(arr) # reassigns array to it's shuffled self?
=> 12: puts arr.last
[1] pry(main)> arr
=> nil
As expected, the value of arr is nil. Let's leave the debugger using the !!! command.
[1] pry(main)> !!!
lee@lees-rig:~$
and fix our code.
# removed the require "pry" as it is no longer needed
def shuffle_if(arr)
if arr.length >= 10 # makes sure our array is shuffled
arr.shuffle! # shuffles in place
else
end
arr = (1..10).to_a # creates a basic array
# removed the binding.pry don't forget and leave them behind!
shuffle_if(arr) # removed reassignment, if shuffling does not occur variable is not assigned a nil value
puts arr.last # now logs a random number between 1 and 10.
That was a quick intro to Pry and debugging Ruby applications. Pry is a great tool and can get you through some weird spots in your code. Recently, the $ method helped me to locate an unexpected name collision. In a future article, we'll address incorporating pry as the rails console and using it to debug Rails applications in development.
further resources
Here are some additional resources for getting the most out of Pry.
- The official Pry Documentation: http://pryrepl.org/
- List of all Pry plugins: https://github.com/pry/pry/wiki/Available-plugins