PRY like a PRO

  • December 19, 2019
  • Pete Whiting
  • 9 min read

by Dan Frenette

TL;DR

The Pry gem is one of the most commonly used tools in the Ruby developer's toolbox, and it has some lesser-known, yet extremely handy, features that will make your experience much better!

Background

The Pry gem is a very popular Ruby REPL and debugging tool; it's even taught as a part of the curriculum in many programming boot camps and online courses on Ruby!

The gem adds a method called #pry onto Ruby's Binding class. Instances of the Binding class encapsulate the context of your current scope to be used again when the scope has changed. In other words, all the variables, methods, and self are stored so they can be used again at a separate time and place in the code.

For example - if you're a user of Rails, have you ever wondered how Rails variables from the controller end up in ERB files in the view? Rails does this by passing binding to an instance of the ERB class behind the scenes.

Pry does something similar, but instead of taking information from the scope to populate an HTML template, it's going to make them available in a REPL! This is how Pry's basic functionality works - exploring the data wherever we throw one of these things as our code is running.

Problem

Most of the time though, developers limit themselves to basic use of the gem: asking what certain variables are and executing code from inside the Pry REPL.

As wonderful as Pry is, even in its basic use, there are several features of the gem that go tragically underused by the majority of developers. Let's take a look at a few:

Solution

Here are some of Pry's lesser-known superpowers, and what you can use them for:

Note: Most of the code examples are formatted using the Awesome Print gem, so if you're curious as to why so many of the lines are prefaced with ap (short for awesome print) or generally why the output looks the way it does, that's why.

!!!

This is great for debugging things when inside of a loop. !!! will force-quit Pry so you don't have to type quit over and over again, or any other tactics you might use to get out of a loop (confession: I used to just restart my whole terminal whenever this happened when before I learned about !!!, and I can confidently say that !!! is the better way to go).

!

While !!! will close out your entire Pry session, using just one ! will clear your input buffer! This one saves me a ton of frustration. Have you ever forgotten to close a bracket or quote before hitting enter? If so, the Pry prompt will look something like this:

~/code % pry
[1] pry(main)> "So you're typing a string, when all of a sudden - AHH, DISTRACTION STRIKES - 
[1] pry(main)* !
Input buffer cleared!
[2] pry(main)> 

Most of the time, adding a new line in the middle of what you were typing isn't actually what you wanted to do. So, in these cases, I use ! to clear the input buffer and get back to the regular prompt. From there, I can use the up arrow to get back to my original line and fix the mistake.

$

Aliases: show-source, show-method

If used without any flags or arguments, $ will make Pry print out whatever body of code represents self. So, for example, if you have your Pry statement in a class, it'll print out the whole class. This command is great for debugging long methods. If that's not specific enough for you, you can pass it the -l flag to include line numbers:

~/code % ruby pry_example.rb

From: /Users/danfrenette/code/pry_example.rb @ line 10 PryExample#hello_world:

     8: def hello_world
     9:   binding.pry
 => 10:   puts "hello world"
    11: end

[1] pry(#<PryExample>)> $ -l

From: pry_example.rb @ line 8:
Owner: PryExample
Visibility: public
Number of lines: 4

 8: def hello_world
 9:   binding.pry
10:   puts "hello world"
11: end
[2] pry(#<PryExample>)> 

But my favorite use for this method is that you can use it to see other methods, even if they're in another class!

~/code % ruby pry_example.rb

From: /Users/danfrenette/code/pry_example.rb @ line 10 PryExample#hello_world:

     8: def hello_world
     9:   binding.pry
 => 10:   puts "hello world"
    11: end

[1] pry(#<PryExample>)> $ AnotherClass#another_method

From: pry_example.rb @ line 33:
Owner: AnotherClass
Visibility: public
Number of lines: 3

def another_method
  puts "I'm somewhere far away from where the breakpoint was placed!"
end
[2] pry(#<PryExample>)> 

whereami

This one's pretty similar to $ -l, but I have a much easier time remembering it for some reason. Additionally, now there's a fat arrow pointing to the line number where your pry statement is located. Neat!

This is great for those long debugging sessions where you end up going down a rabbit hole and forget where you are in your code.

~/code % ruby pry_example.rb

From: /Users/danfrenette/code/pry_example.rb @ line 10 PryExample#hello_world:

     8: def hello_world
     9:   binding.pry
 => 10:   puts "hello world"
    11: end

[1] pry(#<PryExample>)> whereami

From: /Users/danfrenette/code/pry_example.rb @ line 10 PryExample#hello_world:

     8: def hello_world
     9:   binding.pry
 => 10:   puts "hello world"
    11: end

[2] pry(#<PryExample>)> 

wtf

Sometimes you just gotta ask "what the f@#*?" in response to something your code did, and with the wtf method in Pry, you can do so productively!

Use the wtf method to access the stack trace for the last error. By default, wtf will return the first five lines of the stack trace.

~/code % ruby pry_example.rb

From: /Users/danfrenette/code/pry_example.rb @ line 10 PryExample#hello_world:

     8: def hello_world
     9:   binding.pry
 => 10:   puts "hello world"
    11: end

[1] pry(#<PryExample>)> do_not_ever_call_this_method
RuntimeError: WHAT DID I SAY?! Now look what you did!
from pry_example.rb:22:in `do_not_ever_call_this_method'
[2] pry(#<PryExample>)> wtf
Exception: RuntimeError: WHAT DID I SAY?! Now look what you did!
--
0: pry_example.rb:22:in `do_not_ever_call_this_method'
1: (pry):1:in `hello_world'
2: /Users/danfrenette/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/pry-0.12.2/lib/pry/pry_instance.rb:387:in `eval'
3: /Users/danfrenette/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/pry-0.12.2/lib/pry/pry_instance.rb:387:in `evaluate_ruby'
4: /Users/danfrenette/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/pry-0.12.2/lib/pry/pry_instance.rb:355:in `handle_line'
[3] pry(#<PryExample>)>

But if you need to go deeper, you can append question marks to the method call (ex wtf????? ) to add additional lines to the stack trace!

[4] pry(#<PryExample>)> wtf?
Exception: RuntimeError: WHAT DID I SAY?! Now look what you did!
--
0: pry_example.rb:22:in `do_not_ever_call_this_method'
1: (pry):1:in `hello_world'
2: /Users/danfrenette/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/pry-0.12.2/lib/pry/pry_instance.rb:387:in `eval'
3: /Users/danfrenette/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/pry-0.12.2/lib/pry/pry_instance.rb:387:in `evaluate_ruby'
4: /Users/danfrenette/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/pry-0.12.2/lib/pry/pry_instance.rb:355:in `handle_line'
5: /Users/danfrenette/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/pry-0.12.2/lib/pry/pry_instance.rb:274:in `block (2 levels) in eval'
6: /Users/danfrenette/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/pry-0.12.2/lib/pry/pry_instance.rb:273:in `catch'
7: /Users/danfrenette/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/pry-0.12.2/lib/pry/pry_instance.rb:273:in `block in eval'
8: /Users/danfrenette/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/pry-0.12.2/lib/pry/pry_instance.rb:272:in `catch'
9: /Users/danfrenette/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/pry-0.12.2/lib/pry/pry_instance.rb:272:in `eval'

ls -q

Have you ever wanted to see what all the variables and methods in the scope are? ls will print out every variable and method available in the current scope! However, this is pretty excessive in the case of certain files (ActiveRecord models that inherit hundreds of methods, for example), so I recommend the -q flag to limit it to methods and variables defined on in the current class/singleton class.

ruby pry_example.rb

From: /Users/danfrenette/code/pry_example.rb @ line 10 PryExample#hello_world:

     8: def hello_world
     9:   binding.pry
 => 10:   puts "hello world"
    11: end

[1] pry(#<PryExample>)> ls -p
PryExample#methods: hello_world  initialize  super_secret_private_method

There's a handful of other interesting options you can pass to ls as well, such as:

  • -c for constants,
  • -G for grepping out variables and methods by Regex
  • -p for public/protected/private methods (it even color codes them!)

You can list all of them with ls --help

;

If you've ever worked with large Ruby objects like API response hashes, or any other object with excessively large output, you probably know the frustration having to scroll up to find something that got pushed down by the output of that object. Luckily, Pry's got our back here too! Just suffix anything with a ; to mute a command's output in the console.

# without a trailing ;
[1] pry(#<PryExample>)> Array.new(100000)
=> [nil,
nil,
nil,
...

# with a trailing ;
[1] pry(#<PryExample>)> Array.new(100000);
[2] pry(#<PryExample>)> 

caller

This is a super useful Ruby method that will let you look upstream at the code that hit your breakpoint. Calling it returns the current execution stack as an array of strings, with each string representing a different file in the stack and the method that was called from it. Unfortunately, the full execution stack is mostly information about the files that Pry itself is using to create a REPL for you, so I'd recommend following Josh Thompson's advice, and parse through them with something like caller.select {|line| line.include? <current_repo_name>} to maximize its usefulness.

From: /Users/danfrenette/code/pry_example.rb @ line 10 PryExample#hello_world:

     8: def hello_world
     9:   binding.pry
 => 10:   puts "hello world"
    11: end

$ [1] pry(#<PryExample>)> ap caller.select {|line| line.include? "pry_example"}
[
    [0] "pry_example.rb:10:in `hello_world'",
    [1] "pry_example.rb:14:in `calls_hello_world'",
    [2] "pry_example.rb:18:in `calls_the_method_that_calls_hello_world'",
    [3] "pry_example.rb:28:in `<main>'"
]
=> nil

Bonus! If you really want to make Pry your own, you can configure your ~/.pryrc file to set aliases and add extensions for certain libraries!

Pry.commands.alias_command "who_called", caller.select {|line| line.include? <current_repo_name> }.

There are also some gems that integrate with Pry, such as the Awesome Print gem gem I've been using to format Ruby code for this post. You can require the gem in your ~/.pryrc file to set the default formatting of Ruby in Pry (and if you want, IRB and the Rails console).

# ~/.pryrc
require "awesome_print"
AwesomePrint.pry!

Outcome/Takeaways

Pry is an excellent tool that, if you're a Ruby developer, you may even use daily. That said, it's totally within your best interest to learn how to use this tool effectively, and reading up on the wiki, or even some cheat sheets like this one are a great way to do that.

Sources and Further Reading

Interested in building with us?