Skip to content
banister edited this page Feb 14, 2012 · 13 revisions

Roughly a year ago I wrote my first article introducing Pry to the Ruby world. Back then Pry was a very a different creature:  just a simple runtime variant of IRB with a few added extras. In the year that's passed Pry has blossomed, radiating out in a number of  interesting directions. In fact Pry is no longer a single project, but has become an ecosystem of plugins and spinoff projects. This blog post will attempt to 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.

Debugging

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.

However two recent plugins, pry-nav and pry-stack_explorer change this situation, transforming Pry into a fairly full-featured debugger.

This plugin adds the `next`,`step` and `continue` commands to Pry. After requiring this gem you use Pry as normal, placing the `binding.pry` at the point you want to start your session - however entering `next` will advance execution along one line:

[source language="ruby"] [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 [/source]

pry-nav also has support for pry-remote, enabling you to perform remote debugging. Pry-nav should work on all Ruby implementations that implement set_trace_func.

This plugin implements the other side of the debugging coin, namely call-stack navigation. It provides the show-stackframeup, and down commands. From the point a Pry session is started, you can move up the call-stack through parent frames, examine state, and even evaluate code. Unlike some other debuggers, pry-stack_explorer incurs no runtime cost and enables navigation right up the call-stack to the birth of the program.

[source language="ruby"]

[2] (pry) main: 0> show-stack

Showing all accessible frames in stack (5 in total):

=> #0 [method] gamma #1 [method] beta #2 [method] alpha [/source]

The reason pry-stack_explorer and pry-nav are not bundled together as a single gem is because pry-stack_explorer only works on Ruby 1.9.2+ MRI, whereas pry-nav works on Ruby 1.8 as well. Nonetheless we will probably build a meta-gem at some point for the 1.9 MRI users.

When both debugging gems are installed, they work seamlessly together and constitute a decent alternative to ruby-debug, especially on Ruby 1.9.3 where ruby-debug support is not that great.

Alternatively, if your preference is for ruby-debug  you can still make use of Pry with the ruby-debug-pry gem. It provides the `pry` command to open a Pry session in the current debugging context.

Remote sessions

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 network or the Internet. They are particularly useful for getting Pry into places not normally possible, such as apps started with the Pow webserver. They also open 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 was the first plugin to implement remote sessions. It uses the DRb library and so is likely to work on most Ruby versions and implementations. To use, 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 in the context of the binding will start once a client connects.

We set up the server as follows:

[source language="ruby"] require 'pry-remote'

class Foo def initialize(x, y) binding.remote_pry end end

Foo.new 10, 20 [/source]

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.

pry-remote-em is one of the most exciting projects in the Pry ecosystem, it opens up possibilities for multi-user remote-debugging/exploration, as well as educational applications. It is also just fun to interact with other programmers in a live environment, indeed a Ruby variant of the game core war could  easily be built on the pry-remote-em base.

One limitation of pry-remote-em at the moment is the lack of pry-nav support, but this will be added soon.

Starting the pry-remote-em server:

[source language="ruby"] require 'pry-remote-em/server'

class Foo def initialize(x, y) binding.remote_pry_em end end

EM.run { Foo.new 10, 20 } [/source]

And similar to pry-remote, we  connect using the pry-remote-em executable.

Error consoles

As discussed in Avdi's blog post the Lisp and Smalltalk communities have great tools for debugging exceptions. Instead of their programs dying from an unrescued exception, they are presented with an interactive console and given 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. Pry-exception_explorer is particularly powerful as it's enhanced by an aggressive C extension and comes with all the Pry accoutrements.

A specialized version of Pry-exception_explorer for failing tests, called Plymouth is also available.

When an exception is raised it is intercepted and  a Pry session started. The binding for the session is the context where the exception was raised, so the user gets access to all state at the error site. Further,  the user can walk the exception's entire call-stack to isolate the precise cause of the error.

Another feature of pry-exception_explorer is the ability to define exactly when it's triggered. This can be as simple as specifying an exception type, 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:

[source language="ruby"] EE.intercept do |frame, ex| ex.is_a?(ArgumentError) && frame.klass == MyClass && frame.prev.klass == MyCallingClass end [/source]

Pry-exception_explorer has proved to be a very powerful and useful gem, often obviating the need for a bona fide debugger. For a quick demo of it in action check out the following mini screencast.

Plymouth is a specialized error console designed for failing tests. When a test fails, a Pry session starts in the context of the failure. Plymouth also gives you an opportunity to edit the failing test code on disk with the `edit --current` command.

Plymouth is also illustrative of the power of pry-exception_explorer, its functionality is implemented using the pry-exception_explorer API, indeed the following code is all that's required to intercept failing tests on RSpec:

[source language="ruby"] 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 [/source]

Plymouth currently supports the RSpec, Bacon and MiniTest libraries, support for other libraries is coming soon.

What's next for Pry?

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. Pry-exception_explorer and Plymouth demonstrate it's possible to incorporate a level of Smalltalk-style interactivity into the ordinary Ruby development workflow, and we intend to take this even further.

The ultimate goal of the Pry project is to make REPL-driven development a reality for the Ruby language and to get as close as possible to the Smalltalk model as we can without the Smalltalk 'image'. Indeed, a GUI front-end for Pry is also in the planning stage.

Pry has been a huge amount of work over the last year or so, but good progress has been made. Multi-user Pry sessions, full debugging functionality, and remote sessions are now a reality.

We need your help

If you would like to see the Pry project come to fruition please consider making a monetary donation, Pry has a very small core team and working on Pry takes up a substantial portion of their time! 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 be more officially associated with the project through sponsorship, this is very welcome too: please contact the project maintainer to arrange this.

Thanks!

Clone this wiki locally