Content for blog.jerryaldrichiii.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

340 lines
8.1 KiB

---
layout: post
title: Debugging Ruby with the Pry debugger
category: ruby
tags: [ruby, pry, debugging, code]
summary: 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 the `require '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
```ruby
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
- [Pry GitHub Page](https://github.com/pry/pry)
- [Pry Wiki](https://github.com/pry/pry/wiki)
- [YouTube Talk by Luke Bergen](https://www.youtube.com/watch?v=o90CCPjcIKE)