Debug Ruby Applications with pry
Debugging Ruby applications becomes significantly easier when you have tools that provide insight into your code's execution. While Ruby ships with a basic debugger, developers often reach for more powerful alternatives when troubleshooting complex issues.
pry is a runtime developer console for Ruby that replaces the default IRB (Interactive Ruby) shell. It offers syntax highlighting, robust introspection capabilities, and a plugin ecosystem that extends its functionality well beyond basic debugging.
With pry, you can pause execution at any point in your code, inspect objects and variables, navigate the call stack, and even modify running code to test fixes immediately.
This article will walk you through using pry to debug Ruby applications effectively.
Prerequisites
Before you begin, make sure you have:
- Ruby 3.3+ installed on your machine
- Familiarity with basic Ruby syntax and application structure
Setting up the project directory
You'll create a clean workspace for practicing debugging techniques with pry.
Start by creating a directory for the debugging examples and navigate into it:
Ruby's bundler manages gems on a per-project basis, so initialize a new bundle:
This creates a Gemfile where you can specify project dependencies. Open the Gemfile and add pry:
Install the gem:
The gem is now available for your debugging session, isolated from other Ruby projects on your system.
Debugging Ruby scripts with pry from the command line
The pry gem transforms Ruby's debugging experience by providing an enhanced REPL (Read-Eval-Print Loop) with features like command history, syntax highlighting, and powerful introspection tools. You can launch pry directly to debug scripts without modifying your code.
Create a Ruby script that calculates prime numbers. Save this as prime_checker.rb:
Running this normally produces a list of prime numbers up to 50:
To debug this script with pry, you can load it directly into a pry session:
When pry launches this way:
- Your script executes completely before the prompt appears
- All methods and constants from your script remain available in the session
- The pry prompt
[1] pry(main)>indicates you're in the main context - You can call any methods defined in your script interactively
From here, you can interact with your code using pry's command set:
show-sourceor$: Display the source code of a methodls: List methods and variables available in the current scopecd: Navigate into an object's contextwhereami: Show your current location in the codeexit: Leave the pry sessionhelp: Display available commands
These commands let you explore your code's structure interactively. Try examining the prime? method and then press ENTER:
You can test methods directly with different inputs:
The ls command reveals what's available in your current scope:
To see detailed information about a method, including where it's defined:
One of pry's strengths is its ability to navigate into object contexts. You can examine what methods a number responds to:
This displays all methods available on the integer object. Press q to exit the list view, then return to the main context:
You can also evaluate Ruby expressions directly to test logic:
This interactive exploration helps you understand how your code behaves without repeatedly editing and running the script.
Embedding pry breakpoints in your code
Rather than loading your entire script into pry, you can pause execution at specific points by inserting breakpoints directly in your code. This approach works better when:
- You've identified a specific area where bugs likely exist
- You want to inspect variable states at precise moments during execution
- Your application has complex initialization that makes command-line debugging impractical
- You're debugging code that's triggered by external events or user input
Here's how to add a breakpoint to your Ruby code:
When you run this script:
Execution stops at the binding.pry line, dropping you into an interactive session:
Several things happen at this breakpoint:
- Pry displays the surrounding code with line numbers
- An arrow
=>marks the current line where execution is paused - You have access to all local variables in the current scope
- You can inspect method parameters and modify values to test different scenarios
Check the current value of the number parameter:
You can see all local variables in scope:
To continue execution until the next breakpoint or the program ends, use the exit command:
The program will hit the breakpoint again for the next number being checked. You can see the updated value:
Type exit-program to stop execution entirely and exit the script.
Using Ruby's built-in debug gem
Ruby 3.1 introduced a built-in debug gem that works similarly to pry. If you prefer not to add external dependencies, you can use the debugger keyword:
This approach has trade-offs:
- Zero setup: No gems to install or require statements needed
- Different interface: The commands differ from pry's syntax
- Less powerful: Fewer introspection and navigation features
- Standard across environments: Available wherever Ruby 3.1+ runs
For this guide, we'll continue using pry since it offers more debugging capabilities and a better interactive experience.
Navigating execution with step commands
When debugging complex logic, you often need to trace execution step by step. Standard pry doesn't include step commands, but the pry-byebug gem adds this functionality.
Install pry-byebug:
Create a new file that demonstrates nested method calls:
Run the script:
When execution pauses at the breakpoint, pry-byebug automatically positions you at the next executable line:
The step command lets you step into method calls:
You're now inside the multiply method. Check the parameter values:
The next command executes the current line and moves to the next one within the same method:
Check the result that was just calculated:
The finish command completes the current method and returns to the caller:
You're back in calculate_area, and the area variable now holds the return value from multiply:
These navigation commands give you precise control over execution flow, letting you trace exactly how data moves through your program.
Inspecting state with stack navigation
When debugging method chains or nested calls, understanding the call stack becomes critical. Pry's stack navigation commands let you move up and down the call stack to inspect variables at different execution levels.
Create a script that demonstrates a deeper call chain:
Run the script:
Execution pauses inside level_three:
The whereami command shows your current position with more context:
You can access the local message variable:
The up command moves one level up the call stack:
You're now in level_two's context. You can access its local variables:
Move up another level:
Now you can see variables from level_one:
The down command moves back down the stack:
You're back in level_two. The backtrace command shows the complete call stack:
This navigation ability proves valuable when debugging complex applications where understanding the sequence of method calls helps identify where things go wrong.
Final thoughts
As you’ve seen throughout this guide, Pry brings a new level of control and visibility to debugging Ruby applications. Instead of relying on simple print statements or Ruby’s basic debugger, Pry allows you to pause execution, inspect objects, and explore your code interactively.
By integrating tools like pry and pry-byebug, you can step through execution, navigate the call stack, and test fixes in real time. This hands-on approach not only speeds up troubleshooting but also deepens your understanding of how your Ruby code actually runs.
In the end, adopting Pry transforms debugging from a tedious task into a powerful, exploratory process that helps you write cleaner, more reliable Ruby applications.