IRB is a great tool and is perfect for experimenting with small code samples and testing out new ideas. It has some difficulty however when the code samples become a bit larger or you’d like to start an IRB session half-way through a method, for example.
Pry, in some sense, is IRB turned on its head. Instead of having to bring your code to a REPL session (as with IRB) you instead bring a REPL session to your code, see the following:
# test.rb require 'pry' class A def hello() puts "hello world!" end end a = A.new # start a REPL session binding.pry # program resumes here (after pry session) puts "program resumes here."
We then run ruby test.rb
from the command line and the following REPL session begins:
Beginning Pry session for main pry(main)> a.hello hello world! => nil pry(main)> def a.goodbye pry(main)* puts "goodbye cruel world!" pry(main)* end => nil pry(main)> a.goodbye goodbye cruel world! => nil pry(main)> exit Ending Pry session for main program resumes here.
There are a few things to note from above:
- The first is that local variables are available to a Pry session (locals are not available to an irb session when using `irb -r`).
- The second is that when you end a Pry session it returns to the running program; this makes Pry particularly useful for debugging.
Debugging
Pry sits somewhere between using `puts` statements to debug and using a bona fide debugger. It effectively opens up an IRB-like session at the place it’s called and makes all the program state at that point available.
In the example below, the code (sans the `binding.pry`) works as required in Ruby 1.8 but in 1.9 it returns the wrong result – notably check_array([])
displays "ary has content"
; whereas we want it to output "ary is empty!"
We start a Pry session right after the line b = *ary
to try to determine the problem in Ruby 1.9:
def check_array(ary) b = *ary # invoking Pry here for debugging binding.pry if !b puts "ary is empty!" else puts "ary has content" end end check_array([])
When running the above code the following Pry session starts up:
Beginning Pry session for main pry(main)> show-method def check_array(ary) b = *ary binding.pry if !b puts "ary is empty!" else puts "ary has content" end end => nil pry(main)> b => [] pry(main)> test = *[] => [] pry(main)> b = !ary.empty? => false pry(main)> exit Ending Pry session for main ary is empty!
From above the first thing we do is display the code for the method we’re debugging – Pry’s `show-method`
command does this.
We next experiment and find that the behaviour of *[]
has changed in 1.9 (in 1.8 it returned nil
) and this must be the cause of our problems. We then come up with a new test b = !b.empty?
and let the program continue (by typing `exit`) with this new value of b
. It now outputs the expected result.
Note that any changes to state we make in a Pry session persist during the lifetime of the program. This fact also makes Pry useful for interactively modifying runtime state.
Interactively modifying runtime state
It may be convenient to open a Pry session in the middle of a running program. The example below is of a game where we are using Pry to increase the lunar lander’s fuel; we also do a bit of exploration of the runtime state to show off some features of Pry.
Beginning Pry session for # pry(#)> ls [:_, :_pry_,:@state, :@frame_counter, :@playgame] => nil pry(#)> cd @playgame Beginning Pry session for # pry(#):1> ls [:_, :_pry_, :@map, :@font, :@wind, :@objects, :@lander] => nil pry(#):1> cd @lander Beginning Pry session for # pry(#):2> @fuel = 300000 => 300000 pry(#):2> nesting Nesting status: -- 0. # (Pry top level) 1. # 2. # => nil pry(#):2> cd .. Ending Pry session for # => nil pry(#):1> ls --methods [:__binding_impl__, :draw, :initialize, :lander, :level, :map] => nil pry(#):1> cd map Beginning Pry session for # pry(#):2> ls [:_, :_pry_, :@nebula, :@nebula_theta, :@moonscape] => nil pry(#):2> exit-all Ending Pry session for # Ending Pry session for # Ending Pry session for #
From above, Pry makes it easy to navigate runtime state. You can pop in and out of objects, nesting sessions as deeply as you like. The `cd` and `ls` commands are provided to make this navigation seem familiar and natural.
Customizability
Pry is also easily customizable – you can trivially set the input for a Pry session to objects other than `Readline` and `$stdin`; and likewise set the output object to something other than `$stdout`.
Many other features of Pry can also be customized making Pry a perfect choice for implementing custom shells. See Customizing Pry for more information.
An IRB Alternative
Pry can be invoked from the command line using the `pry` executable. It can then be used as an alternative to IRB. Many of the IRB command line options are supported. Type `pry –help` at the command line for more information.
Conclusion
Pry is an IRB-alike that can be invoked at any time and anywhere in the program and on any receiver; it can also receive input from anywhere and send output to anywhere. The examples shown here illustrate just some of the functionality of Pry, read the Wiki for the full story.