Collection of Rails debugging#
Installation#
- Most of this local is copied from Rubyists, It's Time to PRY Yourself Off IRB!
- List of pry plugins
- pry github wiki
% gem install pry pry-doc
puts#
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
end
and, temporarily silence STDOUT
bin/rails server 1>/dev/null
Random commands inside pry#
> pry
`whoami`
self
self.inspect
puts RUBY_VERSION
local_variables
global_variables
self.methods
[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#
Navigating State with cd and ls#
[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? Let’s 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 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.
options:
-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 Pry.config.ls.ceiling)
-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);
rb_ary_modify(ary);
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 arr.map!
Just like show-doc and ?, the equivalent for show-source is $:
[15] pry(main)> $ Array#map!
[16] pry(main)> $ arr.map!
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
subtotals.reduce(:+)
end
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 let’s 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 = @line_items.map { |li| li.quantity * li.price }
binding.pry
subtotals.reduce(:+)
end
Exit the editor, and you would be brought back to the Pry session. Now, let’s 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
10.0
Success! Now we can go ahead and remove binding.pry. But before we do, let’s 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
end
def total
- subtotals = @line_items.each { |li| li.quantity * li.price }
+ subtotals = @line_items.map { |li| li.quantity * li.price }
subtotals.reduce(:+)
end
end
@@ -26,4 +28,4 @@ end
order = Order.new
order.add_line_item LineItem.new(2, 3.00)
order.add_line_item LineItem.new(4, 1.00)
puts order.total
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.new
order.add_line_item LineItem.new(2, 3.00)
order.add_line_item LineItem.new(4, 1.00)
binding.pry
order.add_line_item LineItem.new(1/0, 100)
puts order.total
Then we run the program:
% ruby -r pry order.rb
From: /store/order.rb @ line 30 :
25: end
26:
27: order = Order.new
28: order.add_line_item LineItem.new(2, 3.00)
29: order.add_line_item LineItem.new(4, 1.00)
=> 30: binding.pry
31: order.add_line_item LineItem.new(1/0, 1.00)
32: puts order.total
Let’s 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-0.9.12.2/lib/pry/pry_instance.rb:328:in `evaluate_ruby'
...
9: /usr/local/var/rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/pry-0.9.12.2/lib/pry/pry_instance.rb:231:in `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 LineItem.new(1/0, 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",
"actual_start_at",
"adjusted_planned_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 ...