Debugging with Pry

A PRIMER

Let's start with something simple

$ cat Gemfile
source 'https://rubygems.org'
gem 'pry'

$ cat script.rb
require 'bundler/setup'
Bundler.setup
require 'pry'

class Foo
  def bar
    puts "Bar!"
  end
  def self.bar
    puts "Baz!"
  end
end

a = 2 + 2

binding.pry

ls

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 16 :

    11:   end
    12: end
    13:
    14: a = 2 + 2
    15:
 => 16: binding.pry

[1] pry(main)> ls
self.methods: inspect  to_s
locals: _  __  _dir_  _ex_  _file_  _in_  _out_  _pry_  a
[2] pry(main)> a
=> 4

cd

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 16 :

    11:   end
    12: end
    13:
    14: a = 2 + 2
    15:
 => 16: binding.pry

[1] pry(main)> cd Foo
[2] pry(Foo):1> ls
Foo.methods: bar
Foo#methods: bar
locals: _  __  _dir_  _ex_  _file_  _in_  _out_  _pry_

show-method

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 16 :

    11:   end
    12: end
    13:
    14: a = 2 + 2
    15:
 => 16: binding.pry

[1] pry(main)> show-method Foo#bar

From: script.rb @ line 6:
Owner: Foo
Visibility: public
Number of lines: 3

def bar
  puts "Bar!"
end
[2] pry(main)>

show-method

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 16 :

    11:   end
    12: end
    13:
    14: a = 2 + 2
    15:
 => 16: binding.pry

[1] pry(main)> show-method Foo.bar

From: script.rb @ line 9:
Owner: #<Class:Foo>
Visibility: public
Number of lines: 3

def self.bar
  puts "Baz!"
end

show-method

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 16 :

    11:   end
    12: end
    13:
    14: a = 2 + 2
    15:
 => 16: binding.pry

[1] pry(main)> cd Foo
[2] pry(Foo):1> show-method self.bar

From: script.rb @ line 9:
Owner: #<Class:Foo>
Visibility: public
Number of lines: 3

def self.bar
  puts "Baz!"
end

show-method -s

$ cat script.rb
require 'bundler/setup'
Bundler.setup
require 'pry'

class Foo
  def hello
    "Foo"
  end
end

class Bar < Foo
  def hello
    super + " Bar"
  end
end

binding.pry

show-method -s

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 17 :

    12:   def hello
    13:     super + " Bar"
    14:   end
    15: end
    16:
 => 17: binding.pry

[1] pry(main)> show-method Bar#hello

From: script.rb @ line 12:
Owner: Bar
Visibility: public
Number of lines: 3

def hello
  super + " Bar"
end

show-method -s

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 17 :

    12:   def hello
    13:     super + " Bar"
    14:   end
    15: end
    16:
 => 17: binding.pry

[1] pry(main)> show-method -s Bar#hello

From: script.rb @ line 6:
Owner: Foo
Visibility: public
Number of lines: 3

def hello
  "Foo"
end

wtf?

$ cat script.rb
require 'bundler/setup'
Bundler.setup
require 'pry'

a = [ 1, 2, 3, 4 ]
binding.pry

wtf?

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 17 :

    12:
    13: def baz
    14:   bar
    15: end
    16:
 => 17: binding.pry

[1] pry(main)> baz
RuntimeError: Error!!!
from script.rb:6:in `foo'
[2] pry(main)> wtf?
Exception: RuntimeError: Error!!!
--
0: script.rb:6:in `foo'
1: script.rb:10:in `bar'
2: script.rb:14:in `baz'
3: (pry):1:in `<main>'
4: /Users/matyas/.rvm/gems/ruby-2.3.3/gems/pry-0.10.4/lib/pry/pry_instance.rb:355:in `eval'
5: /Users/matyas/.rvm/gems/ruby-2.3.3/gems/pry-0.10.4/lib/pry/pry_instance.rb:355:in `evaluate_ruby'

// etc

watch

$ cat script.rb
require 'bundler/setup'
Bundler.setup
require 'pry'

class Foo
  attr_reader :counter
  def initialize
    @counter = 1
  end
  def add
    @counter += 1
  end
end

binding.pry

watch

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 15 :

    10:   def add
    11:     @counter += 1
    12:   end
    13: end
    14:
 => 15: binding.pry

[1] pry(main)> foo = Foo.new
=> #<Foo:0x007fc68fa16410 @counter=1>
[2] pry(main)> watch foo.counter
Watching foo.counter
watch: foo.counter => 1
[3] pry(main)> a = 2 + 2
=> 4
[4] pry(main)> foo.add
watch: foo.counter => 2
=> 2

hist

$ cat script.rb
require 'bundler/setup'
Bundler.setup
require 'pry'

a = [ 1, 2, 3, 4 ]
binding.pry

hist

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 6 :

    1: require 'bundler/setup'
    2: Bundler.setup
    3: require 'pry'
    4:
    5: a = [ 1, 2, 3, 4 ]
 => 6: binding.pry

[1] pry(main)> a.map do |value|
[1] pry(main)*   value = value * 2
[1] pry(main)*   value + 5
[1] pry(main)* end
=> [7, 9, 11, 13]
[2] pry(main)> hist
1: a.map do |value|
2:   value = value * 2
3:   value + 5
4: end

hist

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 6 :

    1: require 'bundler/setup'
    2: Bundler.setup
    3: require 'pry'
    4:
    5: a = [ 1, 2, 3, 4 ]
 => 6: binding.pry

[1] pry(main)> a.map do |value|
[1] pry(main)*   value = value * 2
[1] pry(main)*   value + 5
[1] pry(main)* end
=> [7, 9, 11, 13]
[2] pry(main)> hist
1: a.map do |value|
2:   value = value * 2
3:   value + 5
4: end
[3] pry(main)> hist --replay 1..4
=> [8, 9, 10, 11]

hist

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 6 :

    1: require 'bundler/setup'
    2: Bundler.setup
    3: require 'pry'
    4:
    5: a = [ 1, 2, 3, 4 ]
 => 6: binding.pry

[1] pry(main)> a.map do |value|
[1] pry(main)*   value += 5
[1] pry(main)* end
=> [6, 7, 8, 9]
[2] pry(main)> hist --save 1..3 temp_script.rb
Saving history in /Users/matyas/htdocs/pry-talk/temp_script.rb...
History saved.
[3] pry(main)> exit

$ cat temp_script.rb
a.map do |value|
  value += 5
end

shell integration

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 6 :

    1: require 'bundler/setup'
    2: Bundler.setup
    3: require 'pry'
    4:
    5: a = [ 1, 2, 3, 4 ]
 => 6: binding.pry

[1] pry(main)> .cat temp_script.rb
a.map do |value|
  value += 5
end
[2] pry(main)> .ls
Gemfile		Gemfile.lock	script.rb	temp_script.rb

inline editing

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 6 :

    1: require 'bundler/setup'
    2: Bundler.setup
    3: require 'pry'
    4:
    5: a = [ 1, 2, 3, 4 ]
 => 6: binding.pry

[1] pry(main)> edit script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 6 :

    1: require 'bundler/setup'
    2: Bundler.setup
    3: require 'pry'
    4:
    5: a = [ 1, 2, 3, 4, 5 ]
 => 6: binding.pry

inline editing

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 9 :

    4:
    5: def foo
    6:   puts "Foo!"
    7: end
    8:
 => 9: binding.pry

[1] pry(main)> edit foo

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 9 :

    4:
    5: def foo
    6:   puts "Foobar!"
    7: end
    8:
 => 9: binding.pry

disabling pry on future calls

$ cat script.rb
require 'bundler/setup'
Bundler.setup
require 'pry'

a = (1...10).to_a
a.map do |a|
  a += 1
  binding.pry
end

puts a.inspect

disabling pry on future calls

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 7 :

     2: Bundler.setup
     3: require 'pry'
     4:
     5: a = (1..10).to_a
     6: a.map do |a|
 =>  7:   binding.pry
     8:   a += 1
     9: end
    10:
    11: puts a.inspect

[1] pry(main)> exit

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 7 :

     2: Bundler.setup
     3: require 'pry'
     4:
// etc

disabling pry on future calls

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 9 :

     4:
     5: a = (1...10).to_a
     6: a.map do |a|
     7:   a += 1
     8:   binding.pry
 =>  9: end
    10:
    11: puts a.inspect

[1] pry(main)> disable-pry
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Using pry-byebug

$ cat Gemfile
source 'https://rubygems.org'
gem 'pry'
gem 'pry-byebug'

$ cat script.rb
require 'bundler/setup'
Bundler.setup
require 'pry'

def foo
  binding.pry
end

def bar
  foo
end

def baz
  bar
end

baz

BE CAREFUL!

$ cat script.rb
require 'bundler/setup'
Bundler.setup
require 'pry'

a = 2 + 2

# This will not work properly and will either quit or jump to a frame 
# somewhere in pry-byebug code. Always have at least one additional 
# line of code after binding.pry when using pry-byebug

binding.pry 

Pry-byebug: moving around stacktrace

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 7 Object#foo:

    5: def foo
    6:   binding.pry
 => 7: end

[1] pry(main)> up

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 10 Object#bar:

     9: def bar
 => 10:   foo
    11: end

[1] pry(main)> down

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 7 Object#foo:

    5: def foo
    6:   binding.pry
 => 7: end

Pry-byebug: moving around stacktrace

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 7 Object#foo:

    5: def foo
    6:   binding.pry
 => 7: end

[1] pry(main)> frame 3

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 17 :

    12:
    13: def baz
    14:   bar
    15: end
    16:
 => 17: baz

[1] pry(main)> 

Pry-byebug: stepping through code

$ cat script.rb
require 'bundler/setup'
Bundler.setup
require 'pry'

def foo
  binding.pry
  a = 0
  a += 1
  a += 2
  a
end

foo

Pry-byebug: stepping through code

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 7 Object#foo:

     5: def foo
     6:   binding.pry
 =>  7:   a = 0
     8:   a += 1
     9:   a += 2
    10:   a
    11: end

[1] pry(main)> a
=> nil

Pry-byebug: stepping through code

[2] pry(main)> step

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 8 Object#foo:

     5: def foo
     6:   binding.pry
     7:   a = 0
 =>  8:   a += 1
     9:   a += 2
    10:   a
    11: end

[2] pry(main)> a
=> 0

Pry-byebug: stepping through code

[3] pry(main)> step

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 9 Object#foo:

     5: def foo
     6:   binding.pry
     7:   a = 0
     8:   a += 1
 =>  9:   a += 2
    10:   a
    11: end

[3] pry(main)> a
=> 1

Pry-byebug: stepping through code

[4] pry(main)> step

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 10 Object#foo:

     5: def foo
     6:   binding.pry
     7:   a = 0
     8:   a += 1
     9:   a += 2
 => 10:   a
    11: end

[4] pry(main)> a
=> 3

Pry-byebug: breakpoints

$ cat script.rb
require 'bundler/setup'
Bundler.setup
require 'pry'

class Foo
  def bar
    puts "Bar?"
  end

  def baz
    bar
  end
end

binding.pry

foo = Foo.new
foo.baz


Pry-byebug: breakpoints

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 17 :

    12:   end
    13: end
    14:
    15: binding.pry
    16:
 => 17: foo = Foo.new
    18: foo.baz
    19:
    20:
    21:

[1] pry(main)> break Foo#bar

  Breakpoint 1: Foo#bar (Enabled)

  6: def bar
7:   puts "Bar?"
8: end

Pry-byebug: breakpoints

[2] pry(main)> continue

  Breakpoint 1. First hit

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 6 Foo#bar:

 => 6: def bar
    7:   puts "Bar?"
    8: end

[2] pry(#<Foo>)>

Pry-byebug: breakpoints

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 17 :

    12:   end
    13: end
    14:
    15: binding.pry
    16:
 => 17: foo = Foo.new
    18: foo.baz

[1] pry(main)> break 11

  Breakpoint 1: /Users/matyas/htdocs/pry-talk/script.rb @ 11 (Enabled)

       8:   end
     9:
    10:   def baz
 => 11:     bar
    12:   end
    13: end
    14:

Pry-byebug: breakpoints

[2] pry(#<Foo>)> break

  # Enabled At
  -------------

  1 Yes     /Users/matyas/htdocs/pry-talk/script.rb @ 11
[3] pry(#<Foo>)>

THIS MIGHT BE CONFUSING

$ cat script.rb
require 'bundler/setup'
Bundler.setup
require 'pry'

class Foo
  def bar
    puts "Bar?"
  end
end

binding.pry
true #byebug

THIS MIGHT BE CONFUSING

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 12 :

     7:     puts "Bar?"
     8:   end
     9: end
    10:
    11: binding.pry
 => 12: true #byebug

[1] pry(main)> break Foo#bar

  Breakpoint 1: Foo#bar (Enabled)

  6: def bar
7:   puts "Bar?"
8: end

[2] pry(main)> Foo.new.bar
Bar?
=> nil
[3] pry(main)> 
# Breakpoints only work if you let the script continue
# not when you're running the code by hand in REPL.

Bonus: pry-inline

$ cat Gemfile
source 'https://rubygems.org'
gem 'pry'
gem 'pry-byebug'
gem 'pry-inline'

$ cat script.rb
require 'bundler/setup'
Bundler.setup
require 'pry'

a = 10
b = a * 2
c = b ** 3

binding.pry
true #byebug

Bonus: pry-inline

$ ruby script.rb

From: /Users/matyas/htdocs/pry-talk/script.rb @ line 10 :

     5: a = 10 # a: 10
     6: b = a * 2 # b: 20
     7: c = b ** 3 # c: 8000
     8:
     9: binding.pry
 => 10: true #byebug

[1] pry(main)>

There was going to be a slide

explaining why pry-rescue is amazing

 

Unfortunately it doesn't work correctly

with pry-byebug at the moment :(

 

If you are OK with this then definitely give it a try though!

Thank you for listening!

@nerdblogpl

http://nerdblog.pl

Debugging with Pry

By Michał Matyas

Debugging with Pry

  • 682