LJ Archive

At the Forge

Pry

Reuven M. Lerner

Issue #219, July 2012

Interact with your Ruby code more easily with Pry, a modern replacement for IRB.

I spend a fair amount of my time teaching courses, training programmers in the use of Ruby and Python, as well as the PostgreSQL database. And as if my graying hair weren't enough of an indication that I'm older than many of these programmers, it's often shocking for them to discover I spend a great deal of time with command-line tools. I'm sure that modern IDEs are useful for many people—indeed, that's what they often tell me—but for me, GNU Emacs and a terminal window are all I need to have a productive day.

In particular, I tell my students, I cannot imagine working without having an interactive copy of the language open in parallel. That is, I will have one or more Emacs buffers open, and use it to edit my code. But I'll also be sure to have a Python or Ruby (or JavaScript) interpreter open in a separate window. That's where I do much of my work—trying new ideas, testing code, debugging code that should have worked in production but didn't, and generally getting a “feel” for the program I'm trying to write.

Indeed, “feeling” the code is a phenomenon I'm sure other programmers understand, and I believe it's crucial when really trying to understand what is going on in a program. It's sort of like learning a new foreign language. At a certain point, you have an instinct for what words and conjugations should work, even if you've never used them before. Sometimes, when things go wrong, if you have enough experience working with the code, you will have an internal sense of what has gone wrong—where to look and how to fix things. This comes from interacting and working with the code on a day-to-day basis.

One of the advantages of a dynamic, interpreted language, such as Python or Ruby, is that you can use a REPL (read-eval-print loop), a program that gives you the chance to interact with the language directly, typing commands and then getting responses. A good REPL will let you do everything from experimenting with one-liners to creating new classes and modules. You're obviously not going to create production code in such an environment, but you might well create some classes, objects and methods, and then experiment with them to see how well they work.

I have been using both Python and Ruby for a number of years, and I teach classes in both languages on a regular basis. Part of these classes always involves introducing students to the interactive versions of these languages—the python command in the case of Python and irb in the case of Ruby.

About a year ago, one of my Python students asked me what I knew about iPython. The fact is that I had heard of it, but hadn't really thought to check much into the project. At home that night, I was pretty much blown away by what it could do, and I scolded myself for not having tried it earlier. Indeed, if you are a Python programmer and not using iPython in your day-to-day work, you should run to your computer, install it and start to use it. It offers a wide and rich variety of functions that provide specific supports for interacting with the language. Of particular interest to me, when teaching my classes, is the ability to log everything I type. At the end of the day, I can send a complete, verbatim log of everything I've written (which is a lot!) to the students.

I have had a similar experience with Ruby during the past few months. When Pry was announced about a year ago, described as a better version of Ruby's interactive IRB program, I didn't really do much with it. But during the past few weeks, I have been using and thoroughly enjoying Pry. I have incorporated it into my courses, and have—as in the case of iPython—wondered how it could be that I ignored such a wonderful tool for as long as I did.

This month, I take a look at Pry, an improved REPL for Ruby. It not only allows you to swap out IRB, the standard interactive shell for Ruby, but it also lets you replace the Rails console. The console is already a powerful tool, but combined with Pry's ability to explore data structures, display documentation, edit code on the fly, and host a large and growing number of plugs, it really sings.

Pry

Pry is a relative newcomer in the Ruby world, but it has become extremely popular, in no small part thanks to Ryan Bates, whose wonderful weekly “Railscasts” screencasts introduced it several months ago. Pry is an attempt to remake IRB, the interactive Ruby interpreter, in a way that makes more sense for modern programmers.

Installing Pry is rather straightforward. It is a Ruby gem, meaning that it can be installed with:

gem install pry pry-doc

You actually don't need to install pry-doc, but you really will want to do so, as I'll demonstrate a bit later.

I tend to use the -V (verbose) switch when installing gems to see more output on the screen and identify any problems that occur. You also might notice that I have not used sudo to install the gem. That's because I'm using rvm, the Ruby version manager, which allows me to install and maintain multiple versions of Ruby under my home directory. If you are using the version of Ruby that came with your system, you might need to preface the above command with sudo. Also, I don't believe that Pry works with Ruby 1.8, so if you have not yet switched to Ruby 1.9, I hope Pry will encourage you to do so.

Once you have installed Pry, you should have an executable program called “pry” in your path, in the same place as other gem-installed executables. So you can just type pry, and you will be greeted by the following prompt:

[1] pry(main)>

Now you can do just about anything in Pry that you could do in IRB. For example, I can create a class, and then a new instance of that class:

[2] pry(main)> class Person
[2] pry(main)*   def initialize(first_name, last_name)
[2] pry(main)*     @first_name = first_name
[2] pry(main)*     @last_name = last_name
[2] pry(main)*   end
[2] pry(main)* end

Now, you can't see it here, but as I typed, the words “class”, “Person”, “def” and “end” were all colorized, similarly to how a modern editor colorizes keywords. The indentation also was adjusted automatically, ensuring that the “end” words line up with the lines that open those blocks.

Once I have defined this class, I can create some new instances. Here are two of them:


[3] pry(main)> p1 = Person.new('Reuven', 'Lerner')
=> #<Person:0x007ff832949580 @first_name="Reuven", @last_name="Lerner">

[4] pry(main)> p2 = Person.new('Shikma', 'Lerner-Friedman')
=> #<Person:0x007ff8332386c8 @first_name="Shikma",
@last_name="Lerner-Friedman">

As expected, after creating these two instances, you'll see a printed representation of these objects. Now, let's say you want to inspect one of these objects more carefully. One way to do it is to act on the object from the outside, as you are used to doing. But Pry treats every object as a directory-like, or namespace-like, object, which you can set as the current context for your method calls. You change the context with the cd command:

cd p2

When doing this, you see that the prompt has changed:


[14] pry(#<Person>):1>

In other words, I'm now on line 14 of my Pry session. However, I'm currently not at the main level, but rather inside an instance of Person. This means I can look at the object's value for @first_name just by typing that:


[15] pry(#<Person>):1> @first_name
=> "Shikma"

Remember that in Ruby, instance variables are private. The only way to access them from outside the object itself is via a method. Because I haven't defined any methods, there isn't any way (other than looking at the printed representation using the #inspect method) to see the contents of instance variables. So the fact that you can just write @first_name and get its contents is pretty great.

But wait, you can do better than this; @first_name is a string, so let's go into that:


[17] pry(#<Person>):1> cd @first_name
[18] pry("Shikma"):2> reverse
=> "amkihS"

As you can see, by cd-ing into @first_name, any method calls now will take place against @first_name (that is, the text string) allowing you to play with it there. You also see how the prompt, just before the > sign at the end, now has a :1 or :2, indicating how deep you have gone into the object stack.

If you want to see how far down you have gone, you can type nesting, which will show you the current context in the code, as well as the above contexts:


[19] pry("Shikma"):2> nesting 
Nesting status:
--
0. main (Pry top level)
1. #<Person>
2. "Shikma"

You can return to the previous nesting level with exit or jump to an arbitrary level with jump-to N, where N is a defined nesting level:


[25] pry("Shikma"):2> nesting
Nesting status:
--
0. main (Pry top level)
1. #<Person>
2. "Shikma"

[26] pry("Shikma"):2> jump-to 1

[27] pry(#<Person>):1> nesting
Nesting status:
--
0. main (Pry top level)
1. #<Person>

[28] pry(#<Person>):1> exit
=> nil

[29] pry(main)> nesting
Nesting status:
--
0. main (Pry top level)

When I first learned about Pry, I worried that cd and ls were taken for objects and, thus, those commands would be unavailable for directory traversal. Never fear; all shell commands, from cd to ls to git, are available from within Pry, if you preface them with a . character.

Editing Code

Pry supports readline, meaning that I can use my favorite Emacs editing bindings—my favorite being Ctrl-R, for reverse i-search—in the command line. Even so, I sometimes make mistakes and need to correct them. Pry understands this and offers many ways to interact with its shell.

My favorite is !, the exclamation point, which erases the current input buffer. If I'm in the middle of defining a class or a method and want to clear everything, I can just type !, and everything I've written will be forgotten. I have found this to be quite useful.

But, there are more practical items as well. Let's say I want to modify the “initialize” method I wrote before. Well, I can just use the edit-method command:

edit-method Person#initialize

Because my EDITOR environment variable is set to “emacsclient”, this opens up a buffer in Emacs, allowing me to edit that particular method. I change it to take three parameters instead of two, save it and then exit back to Pry, where I find that it already has been loaded into memory:

[52] pry(main)> p3 = Person.new('Amotz', 'Lerner-Friedman')
ArgumentError: wrong number of arguments (2 for 3)
from (pry):35:in `initialize'

Thanks to installing the pry-doc gem earlier, I even can get the source for any method on my system—even if it is written in C! For example, I can say:

show-method String#reverse

and I get the C source for how Ruby implements the “reverse” instance method on String. I must admit, I have been working with open source for years and have looked at a lot of source code, but having the source for the entire Ruby standard library at my fingertips has greatly increased the number of times I do this.

Rails Integration

Finally, Pry offers several types of integration with Ruby on Rails. The Rails console is basically a version of IRB that has loaded the Rails environment, allowing developers to work directly with their models, among other things. Pry was designed to work with Rails as well.

The easiest way to use Pry instead of IRB in your Rails console is to fire it up, using the -r option to require a file—in this case, the config/environment.rb file that loads the appropriate items for the Rails environment. So I was able to run:

pry -r ./config/environment

On my production machine, of course, I had to say:

RAILS_ENV=production pry -r ./config/environment

Once I had done this, I could navigate through the users on my system—for example:

u = User.find_by_email("reuven@lerner.co.il")

Sure enough, that put my user information in the variable u. I could have invoked all sorts of stuff on u, but instead, I entered the variable:

cd u

Then I was able to invoke the “name” method, which displays the full name:


[14] pry(#<User>):2> name
=> "Reuven Lerner"

But this isn't the best trick of all. If I add Pry into my Gemfile, as follows:

gem 'pry', :group => :development

Pry will be available during development. This means anywhere in my code, I can stick the line:

binding.pry

and when execution reaches that line, it will stop, dropping me into a Pry session. This works just fine when using Webrick, but it also can be configured to work with Pow, a popular server system for OS X:

def show
  binding.pry
end   

I made the above modification to one of the controllers on my site, and then pointed my browser to a page on which it would be invoked. It took a little bit of time, but the server eventually gave way to a Pry prompt. The prompt worked exactly as I might have expected, but it showed me the current line of execution within the controller, letting me explore and debug things on a live (development) server. I was able to explore the state of variables at the beginning of this controller action, which was much better and more interactive than my beloved logging statements.

Conclusion

Pry is an amazing replacement for the default IRB, as well as for the Rails console. There still are some annoyances, such as its relative slowness (at least, in my experience) and the fact that readline doesn't always work perfectly with my terminal-window configuration. And as often happens, the existence of a plugin infrastructure has led to a large collection of third-party plugins that handle a wide variety of tasks.

That said, these are small problems compared with the overwhelmingly positive experience I have had with Pry so far. If you're using Ruby on a regular basis, it's very much worth your while to look into Pry. I think you'll be pleasantly surprised by what you find.

Reuven M. Lerner is a longtime Web developer, consultant and trainer. He is also finishing a PhD in learning sciences at Northwestern University. His latest project, SaveMyWebApp.com, went live this spring. Reuven lives with his wife and children in Modi'in, Israel. You can reach him at reuven@lerner.co.il.

LJ Archive