Quantcast
Channel: Planet Apache
Viewing all articles
Browse latest Browse all 9364

J Aaron Farr: Pry Ruby Open

$
0
0

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, and object.: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:


Viewing all articles
Browse latest Browse all 9364

Trending Articles