-
Notifications
You must be signed in to change notification settings - Fork 0
Pry ecosystem
Roughly a year ago, I wrote my first article introducing Pry to the Ruby world. Back then Pry was a very different creature, just a simple runtime variant of IRB with a few added extras. In the year that's passed, Pry has grown from a single project into an ecosystem, with a variety of plugins and spinoffs. In this post I'll describe the current state of the Pry ecosystem, provide a brief synopsis of some of the plugins available, and attempt to chart the course of future development.
Aside from its use as an IRB alternative, Pry's other primary use case is as a
debugger. The Pry gem by itself hasn't any facilities for a ruby-debug style
next
or step
, so any debugging was limited to examining program snapshots
around the binding.pry
call.
Now, however, two recent plugins, pry-nav and pry-stack_explorer, have transformed Pry into a full-featured debugger.
This plugin adds the next
, step
, and continue
commands to Pry. After
requiring this gem, you can use Pry as normal, placing binding.pry
wherever you want to start your session. The next
command will advance execution
by one line:
[1] (pry) main: 0> next
From: ./y.rb @ line 5 in Object#hello:
1: require 'pry-nav'
2:
3: def hello
4: binding.pry
=> 5: x = 20
6: end
7:
8: hello
pry-nav also has support for pry-remote, enabling you to perform remote debugging.
This plugin implements the other side of the debugging coin, call-stack navigation. It provides the show-stack, frame, up and down commands. From the point a Pry session is started, you can move up the call stack through parent stack frames, examine state, and even evaluate code!
[2] (pry) main: 0> show-stack
Showing all accessible frames in stack (5 in total):
--
=> #0 [method] gamma
#1 [method] beta
#2 [method] alpha
While these two gems create a powerful debugger, you may at some point find yourself needing to use ruby-debug. If so, you can embed Pry into it with the ruby-debug-pry gem.
Remote sessions have been another important development in the Pry ecosystem. A remote session allows you to start instances of Pry in a running program and connect to those instances over a socket. This is particularly useful for using Pry in places not normally possible, such as apps running on the Pow web server. It also opens the door for such exotica as multi-user sessions.
Pry has two plugins that implement remote sessions: pry-remote
and the newer
and more ambitious pry-remote-em
.
pry-remote uses the DRb and should work on Ruby versions and
implementations. To use it, simply replace the normal binding.pry
call with
binding.remote_pry
as the example below illustrates. A DRb server will await
connections, and a remote session will start as soon as a client connects.
We set up the server as follows:
require 'pry-remote'
class Foo
def initialize(x, y)
binding.remote_pry
end
end
Foo.new 10, 20
We can then connect to it using the pry-remote
executable.
pry-remote-em is a sophisticated EventMachine-based alternative to pry-remote. It adds user authentication and SSL support along with tab completion and paging. It also allows multiple clients to connect to the same server, and multiple servers to run on the same computer and even within the same process.
I'm very excited about pry-remote-em
, as it opens up possibilities for
multi-user remote debugging and exploration as well as educational applications.
It is also just fun to interact with other programmers in a live environment;
for example, a Ruby variant of [core war][core war] could easily run on the
pry-remote-em
base.
To start the pry-remote-em server:
require 'pry-remote-em/server'
class Foo
def initialize(x, y)
binding.remote_pry_em
end
end
EM.run { Foo.new 10, 20 }
And, similar to pry-remote, we connect using the pry-remote-em
executable.
As discussed in Avdi Grimm's blog post Hammertime: An interactive error console for Ruby, the Lisp and Smalltalk communities have great tools for debugging exceptions. Instead of allowing their programs to die from an un-rescued exception, their programming environments present them with an interactive console and give them an opportunity to correct the problem.
Avdi's nifty Hammertime gem is one attempt to bring this functionality to Ruby. pry-exception_explorer is another. A specialized version of pry-exception_explorer for intercepting test failures, called Plymouth, is also available.
When your program raises an exception, pry-exception_explorer intercepts it and starts a Pry session in the context where the exception was raised. The user has access to all state at the error site. Furthermore, the user can walk up the exception's entire backtrace, evaluating arbitrary code at each level, to isolate the precise cause of the error.
Another feature of pry-exception_explorer is the ability to define exactly which exceptions should be captured. This can be as simple as specifying an exception class, or as sophisticated as an assertion over the entire state of the call stack. Rudimentary support for some C-level exceptions is also provided and activated with a command line switch.
In the example below, we configure a call-stack assertion so that Pry starts when an ArgumentError is raised, but only if the exception context is an instance of MyClass and the parent's context is an instance of MyCallingClass:
EE.intercept do |frame, ex|
ex.is_a?(ArgumentError) && frame.klass == MyClass &&
frame.prev.klass == MyCallingClass
end
Pry-exception_explorer has proven itself to be very useful, often obviating the need for a real debugger. For a quick demo of it in action, check out the following mini screencast.
However, this plugin has been deprecated in favor of pry-rescue.
Plymouth is a specialized error console designed to help debug failing tests.
When a test fails, a Pry session starts immediately at the site of the failure.
You can execute Ruby in the REPL to figure out what's wrong, and you can also
use edit --current
to fix the failed test in your text editor.
Plymouth illustrates the power of pry-exception_explorer; its functionality is implemented using the pry-exception_explorer API. In fact, the following code is all that's required to intercept failing tests on RSpec:
EE.intercept do |frame, ex|
if ex.class.name =~ /RSpec::Expectations::ExpectationNotMetError/
message = ex.message
true
else
false
end
end.skip_until do |frame|
frame.klass.name =~ /RSpec::Core::ExampleGroup::Nested/
end
Plymouth currently supports the RSpec, Bacon and MiniTest libraries. Support for other libraries is coming soon.
The projects described above represent a generous slice of the Pry universe, but there's still a lot more out there—plugins for live syntax highlighting, Rails integration, and bash alias integration, to name just a few. The Pry ecosystem is expanding at an impressive rate.
The ultimate goal of the Pry project is to make REPL-driven development a reality for the Ruby language and to get as close to the Smalltalk model as we can, even to the point of implementing a GUI front-end for Pry.
Pry has been a huge amount of work over the last year or so, but we've made good progress. Multi-user Pry sessions, full debugging functionality, and remote sessions are now a reality.
If you would like to see the Pry project come to fruition, please help us work through the issues. If you're too busy, you might consider making a monetary donation. The core team is small and there's only so much we can do, but with a bit of funding the project can develop at a faster rate and with a higher degree of polish. Alternatively, if your company would like to sponsor the project, you can contact the project maintainer to make arrangements.
I'd like to thank everyone who's used and contributed to Pry over the last year. With your help, I hope this next year will be even better.