Nothing Works the Way You Think It Does. It's a familiar scene: the code doesn't work. Not only does it not work, but it makes no sense whatsoever that it doesn't work. You've checked the code several times, and it's just not possible that it's broken. But it is.
In Ruby, you can debug and test your code using IRB, short for Interactive Ruby, a quick way to explore the Ruby programming language and try out code without creating a file. IRB is a Read-Eval-Print Loop, or REPL (7 Minutes), a tool offered by many modern programming languages. To use it, you launch the irb
executable and type your Ruby code at the prompt. IRB evaluates the code you type and displays the results.
However, IRB is not without its limitations, and this is where Pry comes in. Pry present's itself as a powerful IRB alternative and runtime developer console for Ruby. However, it is much, much more than that.
- Pry (11 Minutes): We use a library called
pry
, which is a replacement forirb
. You'll want to be familiar with it; it's one of the most important debugging tools we use.
gem install pry pry-doc
Install both pry
and pry-doc
. The latter provides MRI Core documentation and source code.
pry
[1] pry(main)> 1 + 1
=> 2
Simply run pry
from the command line.
Pry has many methods; this guide presents with the most important ones to you get familiar with and understand your whereabouts.
The first most important command is the help
.
[1] pry(main)> help
Help
help Show a list of commands or information about a specific command.
Context
cd Move into a new context (object or scope).
find-method Recursively search for a method within a class/module or the current namespace.
ls Show the list of vars and methods in the current scope.
pry-backtrace Show the backtrace for the pry session.
raise-up Raise an exception out of the current pry instance.
reset Reset the repl to a clean state.
watch Watch the value of an expression and print a notification whenever it changes.
whereami Show code surrounding the current context.
wtf? Show the backtrace of the most recent exception.
...
I strongly advise you to read all the commands and try those out.
If you have questions about a particular command, you can also use:
[1] pry(main)> help show-doc
Usage: show-doc [OPTIONS] [METH]
Aliases: ?
Show the documentation for a method or class. Tries instance methods first and
then methods by default.
show-doc hi_method # docs for hi_method
show-doc Pry # for Pry class
show-doc Pry -a # for all definitions of Pry class (all monkey patches)
-s, --super Select the 'super' method. Can be repeated to traverse the ancestors
-l, --line-numbers Show line numbers
-b, --base-one Show line numbers but start numbering at 1 (useful for `amend-line` and `play` commands)
-a, --all Show all definitions and monkeypatches of the module/class
-h, --help Show this message.
Live help system - Pry Wiki (10 Minutes).
The ability to retrieve method documentation is essential when learning a new library or code base. And the ability to read the documentation in a REPL environment, where you can interact with the methods on live code is particularly useful. Pry does this without relying on any external utilities. It simply extracts what it needs at runtime from the source file. To retrieve a method or class's documentation, use the show-doc
command. You can use the show-doc
command in instances or without instances using the class definition. Finally, Pry provides a convenient shortcut for show-doc
– ?
:
[1] pry(main)> show-doc Array#map
From: array.c (C Method):
Owner: Array
Visibility: public
Signature: map()
Number of lines: 12
Invokes the given block once for each element of self.
Creates a new array containing the values returned by the block.
See also Enumerable#collect.
If no block is given, an Enumerator is returned instead.
a = [ "a", "b", "c", "d" ]
a.collect {|x| x + "!"} #=> ["a!", "b!", "c!", "d!"]
a.map.with_index {|x, i| x * i} #=> ["", "b", "cc", "ddd"]
a #=> ["a", "b", "c", "d"]
WARNING: the show-doc command is deprecated. It will be removed from future Pry versions.
Please use 'show-source' with the -d (or --doc) switch instead
Example: show-source Array#map -d
Documentation browsing - Pry Wiki (6 Minutes).
One of Pry's killer features is its ability to display source code for methods and classes. Pry does this without relying on any external utilities. Source browsing is an invaluable tool when you are coming to grips with a new code base, and it comes into its own when the library you are exploring has little or no documentation.
The show-source
command is capable of showing source code for classes/modules and methods. Simply typing show-source method_name
pulls the source for the method and display it with syntax highlighting.
[1] pry(main)> show-source Array#map
From: array.c (C Method):
Owner: Array
Visibility: public
Signature: map()
Number of lines: 13
static VALUE
rb_ary_collect(VALUE ary)
{
long i;
VALUE collect;
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
collect = rb_ary_new2(RARRAY_LEN(ary));
for (i = 0; i < RARRAY_LEN(ary); i++) {
rb_ary_push(collect, rb_yield(RARRAY_AREF(ary, i)));
}
return collect;
}
Source browsing - Pry Wiki (8 Minutes).
The cd
command is used to move into a new object (or scope) inside a Pry session. When inside the new scope, it becomes the self for the session, and all commands and methods will operate on this new self.
As in UNIX shells, you use cd ..
to go back to the previous scope, cd /
to return to the top-level scope for the Pry session (usually main but does not have to be) and cd -
to switch between last two scopes.
This extended cd syntax is known as "object path syntax".
[1] pry(main)> arr = [1, 2, 3]
=> [1, 2, 3]
[2] pry(main)> cd arr
[3] pry(#<Array>):1> self
=> [1, 2, 3]
[4] pry(#<Array>):1> cd ..
[5] pry(main)>
The ls
command is essentially a unified wrapper to a number of Ruby's introspection mechanisms, including (but not limited to) the following methods: methods
, instance_variables
, constants
, local_variables
, instance_methods
, class_variables
and all the various permutations thereof.
[6] pry(main)> cd arr
[7] pry(#<Array>):1> ls
Enumerable#methods:
chain chunk_while detect each_entry each_with_index entries find flat_map grep_v inject max_by min_by partition slice_after slice_when tally
chunk collect_concat each_cons each_slice each_with_object filter_map find_all grep group_by lazy member? minmax_by reduce slice_before sort_by to_set
Array#methods:
& [] bsearch compact! delete_if empty? first inspect map! permutation push reverse select slice take_while uniq
* []= bsearch_index concat difference eql? flatten intersection max place rassoc reverse! select! slice! to_a uniq!
+ all? clear count dig fetch flatten! join min pop reject reverse_each shelljoin sort to_ary unshift
- any? collect cycle drop fill hash keep_if minmax prepend reject! rindex shift sort! to_h values_at
<< append collect! deconstruct drop_while filter include? last none? pretty_print repeated_combination rotate shuffle sort_by! to_s zip
<=> assoc combination delete each filter! index length one? pretty_print_cycle repeated_permutation rotate! shuffle! sum transpose |
== at compact delete_at each_index find_index insert map pack product replace sample size take union
self.methods: __pry__
locals: _ __ _dir_ _ex_ _file_ _in_ _out_ pry_instance
[8] pry(#<Array>):1>
By default typing ls
shows you the local variables defined in the current context and any public methods or instance variables defined on the current object.
State navigation - Pry Wiki (9 Minutes).
Simply add export EDITOR='code -w'
to your .zshrc
or .bashrc
file. You can also use .pryrc
(2 Minutes) file.
The standard (and recommended) way to invoke Pry at runtime is to use binding.pry
. Starting Pry this way ensures that the session inherits all local variables and other relevant states. It also causes the whereami
command to be invoked automatically - and so the surrounding context of the session (and few lines either side of the invocation line) is displayed for the user.
Note that we can put binding.pry
anywhere in our program at the point we want the session to start, and the self
of the session will be the self
at that point.
Create a test.rb
file and put the code below on it:
require 'pry'
class A
def hello() puts "hello world!" end
end
a = A.new
# start a REPL session
binding.pry
# program resumes here (after pry session)
puts "program resumes here."
Now let's execute and edit the class using Pry:
pry test.rb
[1] pry(main)> a.hello
hello world!
=> nil
[2] pry(main)> def a.goodbye
[2] pry(main)* puts "goodbye cruel world!"
[2] pry(main)* end
=> :goodbye
[3] pry(main)> a.goodbye
goodbye cruel world!
=> nil
[4] pry(main)> exit
program resumes here.
Now check the test.rb
file and see that the goodbye
method was added only during runtime, it is not persisted in the file.
Runtime invocation - Pry Wiki (6 Minutes).
When invoking a runtime session on a Binding, the whereami
command is automatically executed. However, the command can also be invoked explicitly by typing whereami
in the REPL.
pry test.rb
[1] pry(main)> cd a
[2] pry(#<A>):1> whereami
Inside #<A>.
Pry gives you access to the most recently caught exception in a local variable called _ex_
. This persists until another exception is raised so that it can be used for detailed digging. The _ex_
variable is a special local (4 Minutes).
pry
[1] pry(main)> 4/0
ZeroDivisionError: divided by 0
from (pry):1:in `/'
[2] pry(main)> _ex_.message
=> "divided by 0"
[3] pry(main)> _ex_.backtrace
=> ["(pry):1:in `/'",
"(pry):1:in `__pry__'",
...
As an alternative to using _ex_.backtrace
you can use the wtf
command to display a few lines of the backtrace for the most recent exception. If you want to see more lines, add more question marks ?
or exclamation marks !
. To see the entire backtrace, pass the -v
(--verbose
) flag, e.g.: wtf -v
.
[4] pry(main)> wtf
Exception: ZeroDivisionError: divided by 0
--
0: (pry):1:in `/'
1: (pry):1:in `__pry__'
2: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/pry_instance.rb:290:in `eval'
3: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/pry_instance.rb:290:in `evaluate_ruby'
4: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/pry_instance.rb:659:in `handle_line'
[5] pry(main)> wtf?
Exception: ZeroDivisionError: divided by 0
--
0: (pry):1:in `/'
1: (pry):1:in `__pry__'
2: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/pry_instance.rb:290:in `eval'
3: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/pry_instance.rb:290:in `evaluate_ruby'
4: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/pry_instance.rb:659:in `handle_line'
5: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/pry_instance.rb:261:in `block (2 levels) in eval'
6: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/pry_instance.rb:260:in `catch'
7: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/pry_instance.rb:260:in `block in eval'
8: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/pry_instance.rb:259:in `catch'
9: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/pry_instance.rb:259:in `eval'
[6] pry(main)> wtf?!?!?!
Exception: ZeroDivisionError: divided by 0
--
0: (pry):1:in `/'
1: (pry):1:in `__pry__'
2: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/pry_instance.rb:290:in `eval'
3: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/pry_instance.rb:290:in `evaluate_ruby'
4: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/pry_instance.rb:659:in `handle_line'
5: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/pry_instance.rb:261:in `block (2 levels) in eval'
6: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/pry_instance.rb:260:in `catch'
7: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/pry_instance.rb:260:in `block in eval'
8: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/pry_instance.rb:259:in `catch'
9: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/pry_instance.rb:259:in `eval'
10: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/repl.rb:77:in `block in repl'
11: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/repl.rb:67:in `loop'
12: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/repl.rb:67:in `repl'
13: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/repl.rb:38:in `block in start'
14: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/input_lock.rb:61:in `__with_ownership'
15: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/input_lock.rb:78:in `with_ownership'
16: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/repl.rb:38:in `start'
17: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/repl.rb:15:in `start'
18: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/pry_class.rb:191:in `start'
19: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/lib/pry/cli.rb:119:in `start'
20: /Users/acastro/.gem/ruby/2.5.1/gems/pry-0.13.1/bin/pry:13:in `<top (required)>'
21: /Users/acastro/.gem/ruby/2.5.1/bin/pry:23:in `load'
22: /Users/acastro/.gem/ruby/2.5.1/bin/pry:23:in `<main>'
[9] pry(main)>
- If you've got any nagging issues inside your code, now is a great time to debug it using
binding.pry
. - Consider switching your default console command to pry in your project. Change the console default to pry.
- Pry Wiki
- Pry Cheatsheet
- Pry Screencast (16 Minutes): Josh Cheek did an excellent screencast on the topic.
- Rubyists, It’s Time to PRY Yourself Off IRB! (20 Minutes).