Pry Debugger Tips

Some basic tips on getting more value from the Pry debugger.

On this page

This post will cover some of the most basic tips on getting more value from the Pry gem.

Source code can be found here

Getting started

We initialize the project demo-pry-debugging, initializer Bundler and add some files:

# Create the project folder
$ mkdir demo-pry-debugging
$ cd demo-pry-debugging
$ touch main.rb

# Initialize the project
$ bundler init
$ bundler add pry --group="development"
$ bundler add pry-doc --group="development"

At this stage, our project is now ready to start working with.

Add some basic code

In main.rb, add the following:

require 'pry'
require 'pry-doc'

class ContrivedMath
  # A simple function to add two numbers
  def add(x, y)
    x + y
  end

  # A simple function to subtract the second
  # argument from the first
  def subtract(x, y)
    x + y
  end
end

def main
  v = ContrivedMath.new
  p v.add(1, 2)
  binding.pry
end

main

Here we require the Pry gems we installed.

Then, we create a contrived class with two basic math functions.

Finally, we define and invoke a main function that creates an instance of our contrived class and calls binding.pry to enter the debugger.

The debugger in action

If we run ruby main.rb, we will enter the Pry debugger once the code hits line 20.

ruby main.rb
3

From: /blog-projects/demo-pry-debugging/main.rb:20 Object#main:

    17: def main
    18:   v = ContrivedMath.new
    19:   p v.add(1, 2)
 => 20:   binding.pry
    21: end

[1] pry(main)>

Once in this state, we can run some commands so that we can see the variables and methods available to us.

First of all, we can use ls -h with -h as the helper flag to see what helpers we have available for listing objects.

[1] pry(main)> ls -h
Usage: ls [-m|-M|-p|-pM] [-q|-v] [-c|-i] [Object]
       ls [-g] [-l]

ls shows you which methods, constants and variables are accessible to Pry. By
default it shows you the local variables defined in the current shell, and any
public methods or instance variables defined on the current object.

The colours used are configurable using Pry.config.ls.*_color, and the separator
is Pry.config.ls.separator.

Pry.config.ls.ceiling is used to hide methods defined higher up in the
inheritance chain, this is by default set to [Object, Module, Class] so that
methods defined on all Objects are omitted. The -v flag can be used to ignore
this setting and show all methods, while the -q can be used to set the ceiling
much lower and show only methods defined on the object or its direct class.

Also check out `find-method` command (run `help find-method`).

    -m, --methods               Show public methods defined on the Object
    -M, --instance-methods      Show public methods defined in a Module or Class
    -p, --ppp                   Show public, protected (in yellow) and private (in green) methods
    -q, --quiet                 Show only methods defined on object.singleton_class and object.class
    -v, --verbose               Show methods and constants on all super-classes (ignores Pry.config.ls.ceiling)
    -g, --globals               Show global variables, including those builtin to Ruby (in cyan)
    -l, --locals                Show hash of local vars, sorted by descending size
    -c, --constants             Show constants, highlighting classes (in blue), and exceptions (in purple).
                                Constants that are pending autoload? are also shown (in yellow)
    -i, --ivars                 Show instance variables (in blue) and class variables (in bright blue)
    -G, --grep                  Filter output by regular expression
    -d, --dconstants            Show deprecated constants
    -h, --help                  Show this message.

Some of the useful flags that I use a lot are -m, -M and -l. If we run ls -l, we can see local variables in this scope:

[2] pry(main)> ls -l
v = #<ContrivedMath:0x00007f8096d9a580>

This tells us that we have the local variable v that is an instance of the ContrivedMath class.

Another useful command is the cd command which allows us to switch context.

[3] pry(main)> cd -h
Usage: cd [OPTIONS] [--help]

Move into new context (object or scope). As in UNIX shells use `cd ..` to go
back, `cd /` to return to Pry top-level and `cd -` to toggle between last two
scopes. Complex syntax (e.g `cd ../@x/@y`) also supported.

cd @x
cd ..
cd /
cd -

https://github.com/pry/pry/wiki/State-navigation#wiki-Changing_scope

    -h, --help      Show this message.

We can use that to switch context to v:

[4] pry(main)> cd v
[5] pry(#<ContrivedMath>):1>

As you can see, we are now in the context of the ContrivedMath class.

We can then use ls -m to see what instance methods are available:

[6] pry(#<ContrivedMath>):1> ls -m
ContrivedMath#methods: add  subtract
self.methods: __pry__

This is a helpful way to see what other functions could be invoked on the object.

We can also find definitions using the ? command.

[7] pry(#<ContrivedMath>):1> ? add

From: main.rb:5:
Owner: ContrivedMath
Visibility: public
Signature: add(x, y)
Number of lines: 5

A simple function to add two numbers

def add(x, y)
  x + y
end

[8] pry(#<ContrivedMath>):1> ? subtract

From: main.rb:10:
Owner: ContrivedMath
Visibility: public
Signature: subtract(x, y)
Number of lines: 6

A simple function to subtract the second
argument from the first

def subtract(x, y)
  x + y
end

This gives us an overview of the function definition as well as the doc block above it (plus some other metadata).

When in doubt and looking for tips, you can also use the help command to display all the options for the Pry gem.

Pry-doc and documentation

Using the pry-doc gem, we can also see the source code of the standard library from within the debugger.

First, return context to the top with cd /, then do the following:

[9] pry(main)> ? Array#map!

From: array.c (C Method):
Owner: Array
Visibility: public
Signature: map!()
Number of lines: 24

Calls the block, if given, with each element;
replaces the element with the block's return value:
  a = [:foo, 'bar', 2]
  a.map! { |element| element.class } # => [Symbol, String, Integer]

Returns a new \Enumerator if no block given:
  a = [:foo, 'bar', 2]
  a1 = a.map!
  a1 # => #<Enumerator: [:foo, "bar", 2]:map!>

Array#collect! is an alias for Array#map!.

static VALUE
rb_ary_collect_bang(VALUE ary)
{
    long i;

    RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
    rb_ary_modify(ary);
    for (i = 0; i < RARRAY_LEN(ary); i++) {
        rb_ary_store(ary, i, rb_yield(RARRAY_AREF(ary, i)));
    }
    return ary;
}

This could be used for any of the standard library methods.

Summary

Today's post demonstrated some of the most useful features of the pry gem and how it can be used to get a better concept of scope and context when debugging.

Resources and further reading

Photo credit: angeloabear