Collection of Rails debugging#


% gem install pry pry-doc


bundle open <gem>   open gem code in editor
gem pristine <gem>  undo all changes from above edit

By default puts outputs to STDOUT

def my_method(*args)
    $stderr.puts "Hello from #my_method

and, temporarily silence STDOUT

bin/rails server 1>/dev/null

Random commands inside pry#

> pry




[2] pry(main)> show-doc Array#map
[3] pry(main)> show-doc Array#map!

> Pry provides a very handy shortcut for show-doc  ?

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

Advanced pry#

[4] pry(main)> arr = [1, 2, 3]
=> [1, 2, 3]

> We can cd into an object, like so:

[5] pry(main)> cd arr
[6] pry(#<Array>):1>

Notice how when we cd-ed into arr, the prompt changes to pry(#):1>. This tells us that the current object is an Array instance, denoted by the # notation. “

[6] pry(#<Array>):1> ls

What else does ls do? Lets ask Pry:
[14] (pry) main: 0> 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*_color, and the separator is 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.


    -m, --methods               Show public methods defined on the Object (default)
    -M, --instance-methods      Show 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
    -g, --globals               Show global variables, including those builtin to Ruby (in cyan)
    -l, --locals                Show locals, including those provided by Pry (in red)
    -c, --constants             Show constants, highlighting classes (in blue), and exceptions (in purple)
    -i, --ivars                 Show instance variables (in blue) and class variables (in bright blue)
    -G, --grep                  Filter output by regular expression
    -h, --help                  Show this message.

Let’s go back to our arr object. Once we are in an object, we can call its methods directly, like so:

[7] pry(#<Array>):1> min
=> 1
[8] pry(#<Array>):1> max
=> 3
[9] pry(#<Array>):1> reverse
=> [3, 2, 1]

Viewing the Source with show-method#

Ever wondered how Array#map! is implemented? Recall that since we are already in an object, we can simply use show-source map! instead of show-source Array#map!.

[10] pry(#<Array>):1> show-source map!

From: array.c (C Method):
Owner: Array
Visibility: public
Number of lines: 12

static VALUE
rb_ary_collect_bang(VALUE ary)
    long i;

    RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length);
    for (i = 0; i < RARRAY_LEN(ary); i++) {
                        rb_ary_store(ary, i, rb_yield(RARRAY_PTR(ary)[i]));
    return ary;

let’s go up one level:

[11] pry(#<Array>):1> cd ..
[12] pry(main)>

The following are all equivalent:

[13] pry(main)> show-source Array#map!
[14] pry(main)> show-source!
Just like show-doc and ?, the equivalent for show-source is $:

[15] pry(main)> $ Array#map!
[16] pry(main)> $!

Part II: Diving deeper into Pry’s Debugging Facilities#

We need to tell Pry what our default editor should be. Create a file called .pryrc in your home directory. Since I love Visual Studio Code:

Pry.config.editor = 'code'

Running a debug session#

ruby -r pry order.rb

Modify the totals method as follows:

def total
  subtotals = @line_items.each { |li| li.quantity * li.price }
  binding.pry # <-- Add this

From: /Users/rambo/Desktop/store/order.rb @ line 14 Order#total:

    12: def total
    13:   subtotals = @line_items.each { |li| li.quantity * li.price }
 => 14:   binding.pry
    15:     subtotals.reduce(:+)
    16: end

[1] pry(#<Order>)>

[1] pry(#<Order>)> ls
Order#methods: add_line_item  line_items  total
instance variables: @line_items
locals: _  __  _dir_  _ex_  _file_  _in_  _out_  _pry_  subtotals

Are our line_items properly populated?

[2] pry(#<Order>)> line_items
=> [#<LineItem:0x007f80d25b62b0 @price=3.0, @quantity=2>,
 #<LineItem:0x007f80d25b6288 @price=1.0, @quantity=4>]

Take a closer look at the total method. Since binding.pry occurs after the subtotals variable, we can certainly access that:

[3] pry(#<Order>)> subtotals
=> [#<LineItem:0x007f80d25b62b0 @price=3.0, @quantity=2>,
 #<LineItem:0x007f80d25b6288 @price=1.0, @quantity=4>]

Making edits#

Making edits
Pry allows you to edit a method without ever leaving the session. We now know we must replace each with a map. So lets do that:

[4] pry(#<Order>)> edit total
You will notice vim (or whatever editor your configured in .pryrc) will launch, with the cursor at the first line of total. Make the necessary changes:

def total
  subtotals = { |li| li.quantity * li.price }
Exit the editor, and you would be brought back to the Pry session. Now, lets see what does subtotals contain:

[1] pry(#<Order>)> subtotals
=> [6.0, 4.0]
Nice! When we exited from our editor, Pry automatically reloaded the file, and again stopped at binding.pry.

Running Shell Commands from Pry#

Before removing binding.pry, we can check if the line after binding.pry works. Since I know the line number, I will go ahead and run the line:

[2] pry(#<Order>)> play -l 15
Success! Now we can go ahead and remove binding.pry. But before we do, lets see what changes we have made:

[3] pry(#<Order>)> .git diff
Pry has the ability to run arbitrary shell commands. All you have to do is prefix the command with a . (dot), like what we did to git diff:

diff --git a/order.rb b/order.rb
index c05aa7d..823ccac 100644
--- a/order.rb
+++ b/order.rb
@@ -10,7 +10,9 @@ class Order

   def total
-    subtotals = @line_items.each { |li| li.quantity * li.price }
+    subtotals = { |li| li.quantity * li.price }

@@ -26,4 +28,4 @@ end
 order =
 order.add_line_item, 3.00)
 order.add_line_item, 1.00)

View Stack Traces with wtf?#

Modify the code by adding another LineItem. To save us a bit of time, let us also put binding.pry before that line.

order =
order.add_line_item, 3.00)
order.add_line_item, 1.00)
order.add_line_item, 100)
Then we run the program:

% ruby -r pry order.rb                                                 

From: /store/order.rb @ line 30 :

    25: end
    27: order =
    28: order.add_line_item, 3.00)
    29: order.add_line_item, 1.00)
 => 30: binding.pry
    31: order.add_line_item, 1.00)
    32: puts
Lets go ahead and play line 31:

[1] pry(#<Order>)> play -l 31
As expected, the program crashes. To see a detailed stack trace, use the wtf? command:

[2] pry(#<Order>)> wtf?
Exception: ZeroDivisionError: divided by 0
0: (pry):6:in `/'
1: (pry):6:in `total'
3: /usr/local/var/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/pry- `evaluate_ruby'
9: /usr/local/var/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/pry- `catch'
To see an even longer stack trace, simply append more ?. For example, wtf??? yields 30 lines.

Analysing Stack Traces with cat --ex#

Pry still has yet another trick up its sleeve. cat --ex directs you to the actual line which threw the exception:

[3] pry(main)> cat --ex

Exception: ZeroDivisionError: divided by 0
From: (pry) @ line 1 @ level: 0 of backtrace (of 15).

 => 1: order.add_line_item, 1.00)
cat --ex also takes in a number as an additional argument, which essentially walks you up the stack trace. This feature is very useful for debugging and tracing larger programs.

More examples#


pry(main)> cd Document.first/@attributes
pry(#<Hash>):2> keys.sort
=> ["actual_finish_at",
"adjusted_planned_start_at", ...

> Or when working in a Rails console:
pry(main)> ls -c --grep Kb Gosu
Kb0 Kb1 Kb2 Kb3 Kb4 Kb5 Kb6 Kb7 Kb8 Kb9 KbA KbB KbBackspace KbC KbD KbDelete KbDown KbE KbEnd KbEnter ...