Pry 102: Advanced Features
Many of you have read my previous posts about the ruby REPL Pry. If not, you should check them out: Give it a Pry and Make It Your Own. During the months since writing those articles, Pry has made major strides in many areas. I'd like to spend the next few minutes going through some of the newer features and re-invigorate your interested in Pry. Remember, just like before, Pry has amazing documentation, helpful maintainers, and solid community backing. So be sure to look around for additional information.

This post is a little code heavy. Beware... :)
1.) Input Buffer Manipulation
# (Section References)
Writing multi-line commands in a REPL can often be quite annoying. Usually, if you make an error on any line, you'll need to rewrite from the beginning. This is why I typically recommend integrating your REPL with a gem like interactive_editor, which gives you a tempfile to edit in your default editor and calls eval() when you close. With Pry, you have these features built in along with several additional features.
Before we move on, let's cover Pry's prompt. The Pry prompt has a lot of information available for you at a glance.
[1] pry(main)>
The number is the current input buffer line number expression number and it will increment as you enter more lines another expression. Many Pry commands will allow you to specify the line you'd like to work with by setting the -i flag and passing the input buffer line number expression number. The word in parenthesis is the current self and can be changed using the cd and cd .. commands.
edit - John Mair asked me to correct my usage of the term input buffer line number. I've made corrections through the post but to summarize: The number in the Pry prompt is not the input buffer line, but rather the expression number. In a multi-line expression (for example a method definition) each line associated is assigned a shared expression number which increments per expression. In short, it's not a line number.
Be sure to set your default editor in your ~/.pryrc file by adding the following line Pry.config.editor = "mate"
Ex. 1 (amend-line)
amend-line command which allows you to modify specific lines in your input buffer.
[1] pry(main)> def hello
[1] pry(main)* puts "Howdy, #{name}"
[1] pry(main)* amend-line 1 def hello(name) # Pry goodness
# 1: def hello(name)
# 2: puts "Howdy, #{name}"
[1] pry(main)* end
# => nil
[2] pry(main)> hello "Jon"
# => Howdy, Jon
If we wanted to edit the method in our default editor instead of using amend-line, we could use the edit command. The edit command acts identically to interactive editor with a few notable exceptions. Without any arguments, edit will open a tempfile and insert the contents of your input buffer. Give it a try:
Ex. 1.1 (Edit)
[1] pry(main)> puts 'blah'
# => blah
[2] pry(main)> edit
# editor opens with `puts 'blah'`
The edit command takes optional arguments to increase its usefulness. Pass -t to open an empty tmpfile.
Ex 1.2 (Edit empty tempfile)
[1] pry(main)> puts 'blah'
# => blah
[2] pry(main)> edit
# editor opens empty tempfile
One of the neatest features of the edit commands is the -ex flag. The -ex flag will open the relevant file at the line that generated the last exception.
Ex. 1.3 (Edit from last exception)
# cat /Users/jjackson/Desktop/hello-exception.rb
# def hello
# raise StandardError
# end
#
[2] pry(main)> hello
# => StandardError: StandardError
# from /Users/jjackson/Desktop/hello-exception.rb:2:in `hello'
[3] pry(main)> edit -ex
# Will open file in editor on line that produced error.
Lastly, using edit with the -i flag you can open a specific line into your editor:
Ex. 1.4 (Edit specific line with edit -i)
[1] pry(main)> puts "Won't be opened"
# Won't be opened
[2] pry(main)> puts "Will be opened."
# Will be opened.
[3] pry(main)> edit -i 2
Pretty sweet amarite?
There are several methods for manipulating the input buffer without using the edit command. To view the contents of your buffer type cat -i <n>, and play them back with play -i <n>
Ex. 1.5 (Print/Play input_buffer)
[1] pry(main)> puts "Blah"
#=> Blah
[2] pry(main)> cat -i # pass line number or range to restrict output
#=> puts "Blah"
[3] pry(main)> play -i 1
#=> Blah
2.) ExceptionzzzzZz
# (Section References)
Pry has a few methods that make dealing with exceptions pretty trivial. The first of these methods is the humorously named wtf?. The wtf? command will show the stack trace of your last exception. For example:
Ex. 2 (Print exception with wtf?)
[1] pry(main)> called_this_method_without_defining_it_first
# NameError: undefined local variable or method `called_this_method_without_defining_it_first' for main:Object
# from (pry):1:in `<main>'
[2] pry(main)> wtf?
# Exception: NameError: undefined local variable or method `called_this_without_defining_it_first' for main:Object
# --
# 0: (pry):1:in `<main>'
# 1: /Users/jjackson/.rvm/gems/ruby-1.9.3-p125/gems/pry-0.9.9.3/lib/pry/# pry_instance.rb:249:in `eval'
# 2: /Users/jjackson/.rvm/gems/ruby-1.9.3-p125/gems/pry-0.9.9.3/lib/pry/pry_instance.rb:249:in `re'
# 3: /Users/jjackson/.rvm/gems/ruby-1.9.3-p125/gems/pry-0.9.9.3/lib/pry/pry_instance.rb:227:in `rep'
# etc..
The next method is cat -ex which allows you to specify the backtrace level you'd like to see. This is great when you'd like to get a little more context from your backtrace. It is called cat -ex <n>, where n is the backtrace level you'd like to explore. For example:
Ex. 2.1 (Print context of exception with cat -ex)
[1] pry(main)> called_this_without_defining_it_first
# NameError: undefined local variable or method `called_this_without_defining_it_first' for main:Object
# from (pry):1:in `<main>'
[2] pry(main)> cat -ex
# Exception: NameError: undefined local # variable or method `called_this_without_defining_it_first' for main:Object
# --
# From: (pry) @ line 1 @ level: 0 of backtrace (of 19).
#
# => 1: called_this_without_defining_it_first
Let me clarify the above code. I called the variable called_this_without_defining_it_first which triggered an exception. Once I had the exception I called cat -ex, which printed the context surounding level 0 of the backtrace. If I needed to see the next level, I would simply passed 1 like so:
Ex. 2.2 (Print context of exception explicitly setting stack level)
[3] pry(main)> cat -ex 1
# Exception: NameError: undefined local variable or method # `called_this_without_defining_it_first' for main:Object
# --
# From: /Users/jjackson/.rvm/gems/ruby-1.9.3-p125/gems/pry-0.9.9.3/lib/pry/pry_instance.rb @ line 249 @ level: 1 of backtrace (of 19).
#
# 244: # moved into the scope of a new Binding (e.g the user typed `cd`)
# 245: inject_sticky_locals(target)
# 246:
# 247: code = r(target)
# 248:
# => 249: result = target.eval(code, Pry.eval_path, Pry.current_line)
# 250: set_last_result(result, target, code)
# 251:
# 252: result
# 253: rescue RescuableException => e
# 254: self.last_exception = e
Which displays the context surounding the next level of the backtrace. This allows you to really dig into your exceptions without all the tedium that normally surrounds the process. +1
3.) Navigation and Exploration
# (Section References)
In Give it a Pry, I talk about how easy it is to become familiar with navigation in Pry. One oft-overlooked feature of Pry navigation that has helped me recently is Pry::NAV_PROMPT, which let's you more easily visualize where you are within your Pry session.
Imagine you have a class that looks like this:
class MyClass
class MySubClass
class MySubSubClass
class Incepted
def self.spinning_top
while true do
puts "**Spinning**"
sleep 2
end
puts "trolololo"
end
end
end
end
end
Now set Pry.config.prompt = Pry::NAV_PROMPT in your ~/.pryrc file. Start up Pry and require MyClass.
Ex. 3 (Using Pry::NAV_PROMPT)
[2] (pry) main: 0> cd MyClass::MySubClass::MySubSubClass::Incepted
[2] (pry) main / MyClass::MySubClass::MySubSubClass::Incepted: 1> spinning_top
#=> **Spinning**
We've changed the context of self and Pry::NAV_PROMPT is allowing us to see how deep we are. In this case, since we changed directly to the innermost class, we see only one context. If we were to cd through these classes individually, we'd see each individual context switch we've made.
Ex. 3.1 (Using Pry::NAV_PROMPT cont.)
[8] (pry) main: 0> cd MyClass
[9] (pry) main / MyClass: 1> cd MySubClass
[10] (pry) main / MyClass / MyClass::MySubClass: 2> cd MySubSubClass
[11] (pry) main / MyClass / MyClass::MySubClass / MyClass::MySubClass::MySubSubClass: 3> cd Incepted
[12] (pry) main / MyClass / MyClass::MySubClass / MyClass::MySubClass::MySubSubClass / MyClass::MySubClass::MySubSubClass::Incepted: 4>spinning_top
#=> **Spinning**
#=> **Spinning**
#
# Now if we wanted to go back into MySubSubClass we could type `exit` or:
#
[12] (pry) main / MyClass / MyClass::MySubClass / MyClass::MySubClass::MySubSubClass / MyClass::MySubClass::MySubSubClass::Incepted: 4> cd ..
[13] (pry) main / MyClass / MyClass::MySubClass / MyClass::MySubClass::MySubSubClass: 3>
# or to exit completely
[16] (pry) main / MyClass / MyClass::MySubClass / MyClass::MySubClass::MySubSubClass: 3> !!!
$~
Pretty awesome stuff here.
Let's assume for a minute that you'd like to explore some of the different methods available to you. Pry enables you to find methods based off of patterns then view and edit them without ever leaving your prompt. We'll continue to use MyClass for simpicity's sake.
Ex. 3.2 (Using find-method)
[2] pry(main)> find-method spinning_top
#
# MyClass::MySubClass::MySubSubClass::Incepted.spinning_top: def self.spinning_top
I just searched for spinning_top without giving Pry any more instructions. It found the exact method I was looking for, dispite being several levels deep. Once again, Awesome!
Now to open that method in my editor:
Ex. 3.3 (edit-method in editor)
[5] pry(main)> edit-method MyClass::MySubClass::MySubSubClass::Incepted.spinning_top
# or
[5] pry(main)> cd MyClass::MySubClass::MySubSubClass::Incepted
[6] pry(MyClass::MySubClass::MySubSubClass::Incepted):1> edit-method spinning_top
Alternatively, with find-method -c you can grep your source code for specific strings. Here we'll look in Pry itself.
Ex. 3.4 (Find method with -c flag to grep for string)
[1] pry(main)> cd Pry
[2] pry(Pry):1> find-method -c save_history
#
# Pry.save_history: def self.save_history
# Pry
# Pry#repl_epilogue: Pry.save_history if Pry.config.history.should_save && # Pry.active_sessions == 0
Sexy.
4.) show-source, _in_, and _out_
# (Section References)
The keywords _file_ and _dir_ are available after using methods like show-source, edit-method, and several others. These special keywords allow you to manipulate the file where methods/classes/modules exist.
Ex. 4 (Using _file_ and _dir_)
[3] pry(main)> show-method Pry#backtrace=
#
# From: /Users/jjackson/.rvm/gems/ruby-1.9.3-p125/gems/pry-0.9.9.3/lib/pry/ pry_instance.rb @ line 27:
# Number of lines: 1
# Owner: Pry
# Visibility: public
#
# attr_accessor :backtrace
[4] pry(main)> _file_
# => "/Users/jjackson/.rvm/gems/ruby-1.9.3-p125/gems/pry-0.9.9.3/lib/pry/pry_instance.rb"
[5] pry(main)> _dir_
# => "/Users/jjackson/.rvm/gems/ruby-1.9.3-p125/gems/pry-0.9.9.3/lib/pry"
To view the contents of the above file you can do something like:
Ex. 4.1 (Using _file_ to view source)
[6] pry(main)> cat #{_file_}
Another amazing feature: passing show-source the -a flag. This will show all active monkey-patches, which is great for when you stumble on code that does things like this:
# cat devil.rb
class TrueClass
def to_s
"false"
end
end
To see this insidiousness:
Ex. 4.2 (Using show-source -a to view active monkey-patches)
[6] pry(main)> show-source -a TrueClass
# Found 2 candidates for `TrueClass` definition:
#
# Candidate 1/2: @ line :
#
#
# Candidate 2/2: (pry) @ line 1:
#
# class TrueClass
# def to_s
# "false"
# end
# end
5.) Remote Debugging with pry-remote && pry-remote-em
# (Section References)
I'm going to briefly run through some really neat features available in Pry from its plugin system. Briefly, because they've been covered in more depth by John Mair on his blog.
pry-remote uses the DRb library and allows you to use calls to binding.pry remotely. All you need to do is use binding.remote_pry and run your program.
require 'pry-remote'
class Hello
def hello(name)
binding.remote_pry
"Howdy, #{name}."
end
end
Hello.new.hello "Jon"
Once you've run your program you simply run pry-remote, which will toss you into a Pry session with the appropriate binding.
Ex. 5 (Using binding.remote-pry)
# From: test.rb @ line 4 Hello#hello:
#
# 4: def hello(name)
# => 5: binding.remote_pry
# 6: "Howdy, #{name}."
# 7: end
#
[1] pry(#<Hello>)>
Alternatively, you could use pry-remote-em, which works similarly to remote-pry, but uses event-machine and has support for authentication and SSL.
6.) Debugging with Pry-nav
# (Section References)
A Pry call to binding.pry is a truly powerful thing, but if you are like most of the Ruby community, you'll find this limiting at some point. Enter pry-nav after installing this plugin and you will have access to next, continue, and step commands from within the context of your bindings.
Ex. 6 (Using Pry-nav)
# Using test.rb from above
$~ ruby test.rb
#
# From: test.rb @ line 4 Hello#hello:
#
# 4: def hello(name)
# => 5: binding.pry
# 6: "Howdy, #{name}."
# 7: end
#
[1] pry(#<Hello>)> next
#
# From: test.rb @ line 4 Hello#hello:
#
# 4: def hello(name)
# 5: binding.pry
# => 6: "Howdy, #{name}."
# 7: end
This allows you to step through your code easily. Alias your commands in your ~/.pryrc file like so:
Pry.commands.alias_command 'c', 'continue'
Pry.commands.alias_command 's', 'step'
Pry.commands.alias_command 'n', 'next'
Conclusions
So, that's all for now folks. Understand that this is still only barely scratching the surface of what Pry can do. It's a REPL with truly wonderful capabilities. If you incorporate it into your daily coding, you'll definitely be glad you did. I hope that this article has convinced some more of you to Give it a Pry.
The Pry team is led by John Mair. See the CONTRIBUTORS file to learn more about them.
Maybe even say thanks for making such an awesome tool. :)
Thanks for reading, be sure to subscribe via RSS (and now email also) to Run With It for more articles. ^_^
Tweet