8.1 KiB
layout | title | category | tags | summary |
---|---|---|---|---|
post | Debugging Ruby with the Pry debugger | ruby | [ruby pry debugging code] | What is Pry? How does it help me debug my Ruby? |
What is Pry?
Pry is an interactive shell that provides an approachable interface to debugging Ruby. Most notably it allows for the ability to transverse a running Ruby program much like one would a filesystem.
Using Pry
Pry can be installed using gem install pry
and be ran directly from your shell using pry
.
By adding require pry
to your Ruby code you can use Pry in your current project. Just requiring Pry will not start a session during execution however. For that, you must also include a breakpoint by adding binding.pry
to your code.
NOTE: I prefer to invoke Pry with a breakpoint on one line (
require 'pry'; binding.pry
). This prevents me from forgetting to remove therequire 'pry'
line from the top of the file and accidentally submitting a PR.
Navigating the Shell
Once inside a Pry shell you can get a list of available commands by running help
.
From that output you can see there are a plethora of executable commands. Don't be discouraged though, I made it for embarrassingly too long using only ls
, step
, puts my_variable_name
, and exit-program
Simple Usage Guide
One thing I wish I had had when starting to use Pry was a "Simple Usage Guide". Below is just such a thing.
Create a Sample File
Create a file called pry_example.rb
with the following contents
def output_hash(input_hash)
puts input_hash
end
def set_baz(input_hash, input_value)
input_hash[:baz] = input_value
end
require 'pry'; binding.pry
some_hash = {}
some_hash[:foo] = 'bar'
output_hash(some_hash)
set_baz(some_hash, 'spam')
puts "Don't execute me"
Starting Pry
Run ruby pry_example.rb
[~/Playground/Blog]$ ruby pry_example.rb
Frame number: 0/1
From: /home/jerry/Playground/Blog/pry_example.rb @ line 11 :
6: input_hash[:baz] = input_value
7: end
8:
9: require 'pry'; binding.pry
10:
=> 11: some_hash = {}
12: some_hash[:foo] = 'bar'
13:
14: output_hash(some_hash)
15:
16: set_baz(some_hash, 'spam')
[1] pry(main)>
As you can see we have halted execution and are at line 11 of our example program
Using step
To execute the current line and proceed to the next type step
[1] pry(main)> step
From: /home/jerry/Playground/Blog/pry_example.rb @ line 12 :
7: end
8:
9: require 'pry'; binding.pry
10:
11: some_hash = {}
=> 12: some_hash[:foo] = 'bar'
13:
14: output_hash(some_hash)
15:
16: set_baz(some_hash, 'spam')
17:
[1] pry(main):1>
Viewing Objects
To verify that line 11 was executed we can inspect the object by typing some_hash
[1] pry(main):1> some_hash
{}
[2] pry(main):1>
We can see here that some_hash
is an empty array.
Run step
again to execute line 12 and set some_hash[:foo]
to bar
[2] pry(main):1> step
From: /home/jerry/Playground/Blog/pry_example.rb @ line 14 :
9: require 'pry'; binding.pry
10:
11: some_hash = {}
12: some_hash[:foo] = 'bar'
13:
=> 14: output_hash(some_hash)
15:
16: set_baz(some_hash, 'spam')
17:
18: puts "Don't execute me"
[2] pry(main)>
Now we can see that some_hash
contains the symbol :foo
which has the value of bar
[2] pry(main)> some_hash
{:foo=>"bar"}
[3] pry(main)>
Using whereami
At any time during our execution we can show lines surrounding us with the command whereami
[3] pry(main)> whereami
From: /home/jerry/Playground/Blog/pry_example.rb @ line 14 :
9: require 'pry'; binding.pry
10:
11: some_hash = {}
12: some_hash[:foo] = 'bar'
13:
=> 14: output_hash(some_hash)
15:
16: set_baz(some_hash, 'spam')
17:
18: puts "Don't execute me"
[4] pry(main)>
After doing this we can see that the method output_hash
is about to be executed
Investigating Methods
We can view the output_hash
method with show-method output_hash
[4] pry(main)> show-method output_hash
From: pry_example.rb @ line 1:
Owner: Object
Visibility: private
Number of lines: 3
def output_hash(input_hash)
puts input_hash
end
[5] pry(main)>
After viewing the method we can step into it using step
[5] pry(main)> step
From: /home/jerry/Playground/Blog/pry_example.rb @ line 2 Object#output_hash:
1: def output_hash(input_hash)
=> 2: puts input_hash
3: end
[5] pry(main)>
We can now see that we are inside the output_hash
method
Technically, we now know that
output_hash
is a procedure and not a method, but such semantics are out of scope for this post.
We can view local variables in output_hash
by using ls
[5] pry(main)> ls
self.methods: inspect to_s
locals: _ __ _dir_ _ex_ _file_ _in_ _out_ _pry_ input_hash
[6] pry(main)>
We can inspect any of these variables with ls
as well
Inspect input_hash
by running ls input_hash
[6] pry(main)> ls input_hash
Enumerable#methods:
all? collect cycle drop_while each_slice redacted
any? collect_concat detect each_cons each_with_index redacted
chunk count drop each_entry each_with_object redacted
Hash#methods:
== clear default= delete_if redacted
[] compare_by_identity default_proc each redacted
[]= compare_by_identity? default_proc= each_key redacted
assoc default delete each_pair redacted
[7] pry(main)>
NOTE: Output here is redacted to shorten line length
As you can see input_hash
is indeed a Hash
Also listed are the available methods for both the Hash and Enumerable classes
Run step
to execute the puts input_hash
line and return to our main code
[7] pry(main)> step
{:foo=>"bar"}
From: /home/jerry/Playground/Blog/pry_example.rb @ line 16 :
11: some_hash = {}
12: some_hash[:foo] = 'bar'
13:
14: output_hash(some_hash)
15:
=> 16: set_baz(some_hash, 'spam')
17:
18: puts "Don't execute me"
[7] pry(main)>
As you can see the output of output_hash
is sent to screen ({:foo=>"bar"}
)
Modifying Code During Runtime
Run step
to go into the set_baz
method
[7] pry(main)> step
From: /home/jerry/Playground/Blog/pry_example.rb @ line 6 Object#set_baz:
5: def set_baz(input_hash, input_value)
=> 6: input_hash[:baz] = input_value
7: end
[7] pry(main)>
Since we are in a running Ruby session we can modify code during runtime
Let's change the value of input_value
to eggs
by running input_value = 'eggs'
[7] pry(main)> input_value = 'eggs'
=> "eggs"
[8] pry(main)>
Now execute line 6 with step
[8] pry(main)> step
From: /home/jerry/Playground/Blog/pry_example.rb @ line 18 :
13:
14: output_hash(some_hash)
15:
16: set_baz(some_hash, 'spam')
17:
=> 18: puts "Don't execute me"
[8] pry(main)>
Now we can see set_baz
set some_hash[:bar]
to eggs
by running puts some_hash;
[8] pry(main)> puts some_hash;
{:foo=>"bar", :baz=>"eggs"}
[9] pry(main)>
TIP: Adding
;
to the end of Ruby commands will suppress extra shell output
Exiting Pry
After running whereami
we see that we do not want to execute line 18
So run exit-program
to end our Pry session
[9] pry(main)> whereami
From: /home/jerry/Playground/Blog/pry_example.rb @ line 18 :
13:
14: output_hash(some_hash)
15:
16: set_baz(some_hash, 'spam')
17:
=> 18: puts "Don't execute me"
[10] pry(main)> exit-program
[~/Playground/Blog]$
Conclusion
As you can see, with just a few simple commands we can do some valuable debugging of our code. I personally use Pry daily in my current job (shakes fist at Ruby's nil
errors)
While this post just scratches the surface of Pry, I highly recommend that you dig deeper. Learning more about Pry will significantly reduce headaches and increase efficiency.
Extra Resources
Below are some extra resources that I've found helpful for learning Pry