Requiring a reasonable Ruby REPL? Give Pry a try. It’s a gem install away with Rails integration not far behind. Today I give you a tour of Pry so you can decide if its right for you.
In what follows, I show my personalized Pry preferences, .pryrc, and workflow. Pry differs a little bit of the box, but I will show you how to get set up and I’ll tell you where what I do differs from the default.
Basics
I like having an uncluttered prompt:
Desktop> pry
? 3 + 4
7
Syntax: _
contains the result of the last expression:
? 2 * _
14
Syntax: expr;
suppresses the result:
? puts "hello, world"
hello, world
nil
? puts "hello, again";
hello, again
? x = (1..10).to_a
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
? y = (1..1000).to_a;
? y.size
1000
Definitions can continue across lines:
? def perimeter(a, b, c)
| a + b + c
| end
nil
? perimeter(3, 4, 5)
12
Command: !
throws away multiline input:
? def area(a, b, c)
| oops
| !
Input buffer cleared!
Command: edit
invokes your editor copying in the current multiline expression:
? def area(a, b, c)
| s = semiperimeter(a, b, c)
| Math.sqrt(s *
| edit
Now in your editor (I set Pry to use TextMate) finish your definition:
def semiperimeter(a, b, c)
perimeter(a, b, c) / 2.0
end
def area(a, b, c)
s = semiperimeter(a, b, c)
Math.sqrt(s * (s - a) * (s - b) * (s - c))
end
Save. Close. Try it out:
? area(3, 4, 5)
6.0
? area(3, 5, 6)
7.483314773547883
Command: .
shell-command invokes the shell from inside Pry.
? .whoami
wtaysom
Use #{
expr}
for interpolation:
? .echo #{x}
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Avoid string literals though:
? .echo #{"hi"}
SyntaxError: <main>: syntax error, unexpected $undefined
".echo #{\"hi\"}"
^
<main>: syntax error, unexpected $end, expecting '}'
".echo #{\"hi\"}"
^
from /Users/wtaysom/.rvm/gems/ruby-1.9.2-p0/gems/pry-0.9.7.4/lib/pry/command_processor.rb:75:in `eval'
Command: help
shows info about Pry commands.
? help hist
Show and replay Readline history. Type `hist --help` for more info. Aliases: history
Navigation
Pry allows you to freely navigate to different scopes. Make self
whatever you want.
Command: cd
expr changes the current scope.
? self
main
? cd x
? self
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
? size
10
? group_by{|i| i % 3}
{1=>[1, 4, 7, 10], 2=>[2, 5, 8], 0=>[3, 6, 9]}
Command: cd ..
returns you to the previous scope.
? cd ..
? self
main
Command: cd
expr/
expr/
… delves deep into an object:
? cd x/size/even?
? self
true
Command: nesting
shows you where you are:
? nesting
Nesting status:
--
0. main (Pry top level)
1. #<Array>
2. 10
3. true
Command: cd /
takes you back to the top:
? cd /
? self
main
Inspection
With cd
to move around, we ls
to look around.
Command: ls [-m|-M|-p|-pM] [-q|-v] [-c|-i] [-g] [-l] [Object]
displays lists of methods, variables, and constants – many convenient selection options:
? ls
self methods: include private public to_s
locals: _ _dir_ _ex_ _file_ _in_ _out_ _pry_ version x y
Asside: The underscored variables are used by Pry.
? ls --globals
global variables: $! $" $$ $& $' $* $+ $, $-0 $-a $-d $-F $-i $-I $-K $-l $-p $-v $-W $-w $. $/ $0 $1 $2 $3 $4 $5 $6 $7 $8 $9 $: $; $< $= $> $? $@ $\ $_ $` $CODERAY_DEBUG $DEBUG $FILENAME $fileutils_rb_have_lchmod $fileutils_rb_have_lchown $KCODE $LOAD_PATH $LOADED_FEATURES $NO_CONTINUATION_WARNING $PROGRAM_NAME $SAFE $stderr $stdin $stdout $VERBOSE $~
? ls 42
Comparable methods: between?
Numeric methods: +@ abs2 angle arg coerce conj conjugate eql? i imag imaginary nonzero? phase polar pretty_print pretty_print_cycle quo real real? rect rectangular remainder singleton_method_added step to_c
Integer methods: ceil chr denominator downto floor gcd gcdlcm integer? lcm next numerator ord pred rationalize round times to_i to_int to_r truncate upto
Fixnum methods: % & * ** + - -@ / < << <= <=> == === > >= >> [] ^ abs div divmod even? fdiv magnitude modulo odd? size succ to_f to_s zero? | ~
? ls 42 --grep ar
Numeric methods: arg imaginary polar rectangular
? ls Array
Array.self methods: [] try_convert
? ls Comparable
? ls --module Comparable
Comparable methods: < <= == > >= between?
? ls --help
... usage, explanation, options ...
Looking around is fine. Looking at is better.
Command: stat [OPTIONS] [METH]
displays basic information about a method.
? stat Comparable#between?
--
Name: between?
Owner: Comparable
Visibility: public
Type: Unbound
Arity: 2
Method Signature: between?(arg1, arg2)
Source Location: Not found.
Command: show-doc [OPTIONS] [METH]
displays documentation (rdoc and YARD) for a method.
? show-doc Comparable#between?
From: compar.c in Ruby Core (C Method):
Number of lines: 8
Owner: Comparable
Visibility: public
Signature: between?(arg1, arg2)
Returns false if obj <=>
min is less than zero or if anObject <=>
max is greater than zero, true otherwise.
3.between?(1, 5) #=> true
6.between?(1, 5) #=> false
'cat'.between?('ant', 'dog') #=> true
'gnu'.between?('ant', 'dog') #=> false
? help show-doc
Show the comments above METH. Type `show-doc --help` for more info. Aliases: ?
? ? Comparable#<
From: compar.c in Ruby Core (C Method):
Number of lines: 2
Owner: Comparable
Visibility: public
Signature: <(arg1)
Compares two objects based on the receiver's <=>
method, returning true if it returns -1.
Command: show-method [OPTIONS] [METH]
displays source (Ruby and C) for a method.
? show-method Comparable#<
From: compar.c in Ruby Core (C Method):
Number of lines: 8
Owner: Comparable
Visibility: public
static VALUE
cmp_lt(VALUE x, VALUE y)
{
VALUE c = rb_funcall(x, cmp, 1, y);
if (rb_cmpint(c, x, y) < 0) return Qtrue;
return Qfalse;
}
? help show-method
Show the source for METH. Type `show-method --help` for more info. Aliases: $, show-source
? $ area
From: (pry) @ line 9:
Number of lines: 4
Owner: Object
Visibility: private
def area(a, b, c)
s = semiperimeter(a, b, c)
Math.sqrt(s * (s - a) * (s - b) * (s - c))
end
Command: show-command [OPTIONS] [CMD]
displays source for a Pry command.
? show-command get-naked
From: /Users/wtaysom/.rvm/gems/ruby-1.9.2-p0/gems/pry-0.9.7.4/lib/pry/default_commands/easter_eggs.rb @ line 11:
Number of lines: 9
command "get-naked" "" do
text = %{
--
We dont have to take our clothes off to have a good time.
We could dance & party all night And drink some cherry wine.
-- Jermaine Stewart }
output.puts text
text
end
MethodFinder
The MethodFinder gem is an injection of some super spiffy Smalltalk juice which I long ago _why-ified. (Source at the bottom of the page.)
Method: object.what?(*args, &block)
finds a method based on its arguments and result.
? 10.what?(3) == 1
[:%, :<=>, :>>, :[], :gcd, :modulo, :remainder]
? 10.find_method(1, 3)
[:%, :<=>, :>>, :[], :gcd, :modulo, :remainder]
? 4.what?(6) > 5
[:*, :**, :+, :<<, :lcm, :|]
? 4.find_method{|r| 4.unknown(6) > 5}
[:*, :**, :+, :<<, :lcm, :|]
Method: object.find_method
helps you find all the methods that have some effect on the receiver.
? [1, 2, 3].find_method{|r| r.unknown(1); r == [1, 3]}
[:delete_at, :slice!]
Modification
Pry integrates with your editor to make modifying files and methods particularly pleasant.
Command: edit [--no-reload|--reload] [--line LINE] [--temp|--ex|FILE[:LINE]|--in N]
opens a file in your editor.
? edit t.rb
In your editor write:
puts "hello, world"
Save and close:
hello, world
? edit t.rb
Append:
raise "oops"
puts "okay"
Save and close:
hello, world
RuntimeError: oops
from /Users/wtaysom/Desktop/t.rb:2:in `<main>'
Command: cat --ex
show context of the last exception.
? cat --ex
Exception: RuntimeError: oops
--
From: /Users/wtaysom/Desktop/t.rb @ line 2 @ level: 0 of backtrace (of 26).
1: puts "hello, world"
=>2: raise "oops"
3: puts "okay"
? edit --ex
See that it takes you right there. Replace line with:
def meaning
42
end
Save and close:
hello, world
okay
? meaning
42
Command: edit-method [OPTIONS] [METH]
jumps directly to a method for editing it.
? edit-method meaning
Replace with:
def meaning
6 * 9
end
Save and close:
hello, world
okay
? meaning
54
William’s Command: reload [FILE]
loads or reloads a specified file.
? r
hello, world
okay
It infers t.rb since t.rb is the only ruby file in the directory. Create a new file s.rb:
puts "wow"
Save:
? r
hello, world
okay
Since we already loaded t.rb.
? r s
wow
? r
wow
I find edit
and edit-method
are good for big projects whereas reload
is good when you’re working on a specific file.
Runtime Invocation
You can go from Pry to editing code, but you can also invoke Pry from code.
Method: binding.pry
opens a Pry session with a given binding.
Replace the contents of s.rb:
@receiver = 10
@args = [3]
@block = nil
def what_pretty_inspect
args = @args.pretty_inspect
block = @block ? "{}" : ""
"#{@receiver.pretty_inspect}.what?(#{args})#{block}"
end
puts what_pretty_inspect
Try it out:
? r
10
.what?([3]
)
? edit-method what_pretty_inspect
Change and save (but don’t close):
def what_pretty_inspect
args = @args.pretty_inspect
block = @block ? "{}" : ""
"#{@receiver.pretty_inspect}.what?(#{args})#{block}"
end
Instead switch back to Pry and press ctrl+c to have Pry continue:
^C
From: s.rb @ line 8 in Object#what_pretty_inspect:
3: @block = nil
4:
5: def what_pretty_inspect
6: args = @args.pretty_inspect
7: block = @block ? "{}" : ""
=> 8: binding.pry
9: "#{@receiver.pretty_inspect}.what?(#{args})#{block}"
10: end
11:
12: puts what_pretty_inspect
? args
"[3]\n"
? block
""
Command: whereami
prints the code context for your current binding:
? whereami
From: s.rb @ line 8 in Object#what_pretty_inspect:
3: @block = nil
4:
5: def what_pretty_inspect
6: args = @args.pretty_inspect
7: block = @block ? "{}" : ""
=> 8: binding.pry
9: "#{@receiver.pretty_inspect}.what?(#{args})#{block}"
10: end
11:
12: puts what_pretty_inspect
Command: exit
expr continues execution from where Pry was invoked using the result of expr as a return value.
? exit
10
.what?([3]
)
Edit s.rb:
def what_pretty_inspect
args = @args.pretty_inspect
block = @block ? "{}" : ""
args.pry
"#{@receiver.pretty_inspect}.what?(#{args})#{block}"
end
Method: object.pry
opens a Pry session in the context of object
with scratch local bindings.
? r
? self
"[3]\n"
? sub!(/^[/, '')
"3]\n"
? sub!(/]\n$/, '')
"3"
Now we’re ready fix up s.rb:
def pretty_inspect
args = @args.pretty_inspect
args.sub!(/^[/, '')
args.sub!(/]\n$/, '')
block = @block ? "{}" : ""
"#{@receiver.pretty_inspect.chomp}.what?(#{args})#{block}"
end
Benchmarking
I use Ruby’s standard Benchmark library to add basic method timing:
? time{$million = (1..1_000_000).to_a}
user system total real
3.870000 0.660000 4.530000 ( 4.532017)
false
? time{$million.reverse}
user system total real
0.410000 0.190000 0.600000 ( 0.605268)
false
? time{$million.reverse!}
user system total real
0.160000 0.000000 0.160000 ( 0.165119)
false
Plugins
Pry recognizes gems starting with “pry-” as plugins. These are are a few interesting ones:
- pry-nav adds step, next, and continue commands.
- pry-syntax-hacks adds some accessor “macros”:
object.@ivar
,object.!private-method
, andobject.:method_object
. - pry-stack_explorer moves up and down the stack. Its a good debugging idea. This plugin takes a stab at it but seemed a little unstable to me.
Wishlist
Two things I hope to see soon:
- Good Ruby Debug integration.
- A command to pretty_inspect all locals and instance variables. (Probably easily accomplished.)
May your prying yield many gems. Happy Holidays!
Apendix A (Source)
A MethodFinder addition:
My .pryrc: