Pry for Ruby— Beginner’s Guide as a REPL and Debugger

What Is Pry?

Pry is a multi-purpose REPL (Read-Evaluate-Print-Loop) tool for Ruby. In other words, it is an alternate, much richer equivalent to IRB which enables you not only to evaluate expressions on-the-fly (with syntax highlighting, unlike IRB), but to delve into your application to debug on a line-by-line basis. Pry’s founding team go even further and use it as their initial tool in software development — in a style known as REPL-Driven Development.

Installation

Without Bundler:

gem install pry pry-doc --no-ri --no-rdoc

Pry Doc is a plugin for Pry which gives extra documentation support.

With Bundler, simply add it to your Gemfile as usual then runbundle install.

You can enter Pry by using the pry command in your terminal, just like the irb command.

Using Pry to View Standard Ruby Documentation

The first major advantage that Pry has over IRB (if you also have Pry Doc installed) is its ability to show you Ruby documentation and source code without needing to go anywhere else. Simply entershow-doc or ? before an object, or object call to a method, to display the program’s documentation for that object or method. You can even do the same for a standard Ruby object such as String#each_line, which will display MRI (Matz’ Ruby, i.e. the standard, reference Ruby) documentation (also found at, for example, RubyDoc).

Even more usefully, often, show-source or $ and show-methodshows the underlying source code for the object or method.

As ? and $ can be used both for the standard Ruby language, and also within your specific Ruby projects and programs, these are useful lookup functions.

Using Pry to Review and Amend Your Program

Using the same commands above, you are able to view your own program’s objects and even amend them as you go.

From the root directory of your program, in your terminal, enter the command pry -r file-path(where -r is shorthand for the -require flag). The Pry session will commence.

I am currently working on developing a Battleships game. The ‘game’ object is created at the bottom of my source code itself assigned to the instance variable @dev_game. I can ‘enter’ this object within Pry by typing cd @dev_game, and Pry will now be one level down ‘inside’ this game object. I can then cd further down into objects within my game object, such as my game_grid object, which is instantiated whenever a game object is created.

If I type ls while inside an object, the object’s methods and variables will be displayed to me. I can call these directly within Pry, or edit them directly using the edit command.

As you can see, Pry has modelled its commands on Linux commands to make the experience as familiar and easy for users as possible. These features enable you to delve into objects directly to ‘see’ what they see and edit anything wrong.

As a Debugger

Pry can be invoked at runtime, at any point in your program’s execution by entering binding.pry at the point at which you want your program to halt. After this point, you are able to ‘step through’ each line of the rest of the execution process of the program.

This feature makes Pry a powerful debugging tool with Ruby. With IRB, you commonly must copy and paste pieces of code in order to undertake a similar process, which can be messy and complicated if such code relies on code in other places of the program. No such complication with Pry and stepping through your methods and code is as easy as entering the line above wherever the error that you wish to resolve is occurring and stepping through.

The most useful commands for me so far when using Pry as a debugger have been:

step = step through to the next line of the program.

next = skip the current line of the program.

play = to return the value of the current expression while remaining on it.

edit = to edit.

whereami = to show you the line you are on, plus a few lines above and below to provide context.

Example Debugging — First Use

In a testament to the power of Pry, I was able to put it use as soon as I had learnt the basics of how to use it. One of my stories this week was to correct an error that had occurred during my previous Iteration Planning Meeting when showcasing new features in my Battleships program. I had been tasked with debugging my data input validation methods. In my version of the Battleships game, input needs to be in the format ‘letter-number’, corresponding to coordinates on the game grid — for example, ‘A1’ or ‘D8’. Such coordinates must not have been previously entered, nor outside of the range of the grid, or else the user should be told that their first input was not in the correct format and that a new input should be entered.

I had developed a method to check the data entered to make sure that it was in the correct letter-number format, within range, and had not been previously entered. The data-checking itself was correct, but after requesting a second input, the game would misfire and not mark the (let’s imagine, correct) second input correctly, either skipping it completely or putting an icon in the wrong place in the grid, in no obvious pattern. Although strictly speaking I had fixed the data-checking methods, it was pretty embarrassing that I had not discovered this directly consequential bug myself before the meeting!

Anyway, I knew that Pry could be used to delve directly into the source code itself, to step through the problem and see what was happening.

You can review my Battleships game file here, which includes the buggy code beginning with the take_user_input method.

I thought I had been ever-so-clever by using recursion to repeatedly ask the user for inputs until one was entered which was both in the correct format and had not been previously entered. At this point, the input_validation method was supposed to return to take_user_input the final, correct input, which itself would return this to take_and_convert_user_input, setting the game’s current target grid point. This would then go on to be processed to work out whether it was a hit or a miss and so on.

After (a lot of) researching, I figured out that what I needed to do with Pry was to use a binding to step through my method and see what was going wrong. I inserted binding.pry just underneath def take_user_input and began a Pry session by typing pry -r ./lib/game.rb into the terminal while in the Battleships root directory. The -r flag stands for -require — to require my Battleships game file within the Pry session. In my source code, I have already created an instance variable of a BattleshipsGame object as follows: @dev_game = BattleshipsGame.new. So now within Pry, I type @dev_game.play_game to begin a game within the session.

The game plays through until the take_user_input method is called, at which point the binding.pry signifies a break-point and you are shown the current section of code with an arrow pointing to the next line.

Entering thestep command into Pry will take you line by line through your code, and at any point you can type the name of any variable (instance, local or anything) to ‘pry out’ its value.

Before using Pry, I was finding it tough to work out what was going wrong with my input methods simply by stepping through the process myself in my mind. Using Pry, I was able to root out my problem — once the correct input had been entered, I had expected my program to return as if beginning anew to the top of the same method, but now with the final input entered, forgetting all the other inputs. This is not how recursion works — instead, with each new call to the take_user_input method, the program was going another level deeper into the memory stack. This means that when the user finally did enter their input in the correct format, such input would disappear as soon as that deepest-level method finished, and then the next method up would complete, all the way up to the top level method — ending up returning the very first (incorrect) input that the user had entered. My game would then attempt to convert this into grid coordinates, with understandably odd results.

As I was refactoring this collection of input validation methods, the Pry step-through command helped me again at each stage of the evolution. I created a local variable before the beginning of the recursion loop and assigned the each new input to it. The loop completes only when the input validation method returns true (signifying that the input is in the correct format) and the local variable’s value is then returned by the top-level method in whatever its final state was (i.e. the correct, final input). All the other inputs are simply not saved.

Further Avenues for Pry

The above demonstrates only the most basic abilities of Pry. There is also the Pry Plus gem which enables you do clever things with Ruby testing frameworks (debugging tests directly as soon as one fails). The maintainers of Pry even use it for ‘REPL-Driven Development’ — see a talk on this here. Their method of development centres on using the REPL first to develop the code you need, then writing tests for it, and finally writing the source code itself. I am intrigued by the approach and will certainly be investigating further!

--

--