Overview
Pry replaces Ruby's standard IRB REPL with an enhanced interactive environment that provides runtime code inspection, object exploration, and debugging capabilities. Ruby loads Pry as a gem that intercepts method calls and provides immediate access to the current binding context.
The core Pry system operates through the Pry
class and associated command processors. When invoked, Pry captures the current execution context, including local variables, method definitions, and object state. The REPL accepts Ruby expressions and executes them within this captured binding.
require 'pry'
def calculate_total(items)
binding.pry # Execution pauses here
items.sum { |item| item[:price] * item[:quantity] }
end
calculate_total([{price: 10, quantity: 2}, {price: 5, quantity: 3}])
Pry implements a command system that extends beyond Ruby evaluation. Commands prefixed with periods execute Pry-specific functionality for code navigation, documentation access, and debugging operations. The Pry::Command
class hierarchy defines these built-in commands.
The binding inspection mechanism works through Ruby's Binding
class, capturing the lexical scope at the point where Pry starts. This includes access to local variables, instance variables, method definitions, and constants available in that scope.
class User
def initialize(name)
@name = name
binding.pry # Access to @name and local context
end
end
Basic Usage
Starting a Pry session requires calling binding.pry
within Ruby code or launching pry
as a standalone REPL. The binding.pry
call creates a breakpoint that halts execution and opens an interactive session with access to the current scope.
require 'pry'
def process_order(order_data)
validated_data = validate_order(order_data)
binding.pry # Inspect validated_data and order_data
create_order(validated_data)
end
The Pry prompt displays the current context, showing the class or module scope and nesting level. Commands execute by typing them at the prompt. Basic navigation uses cd
to change context between objects and ls
to list available methods and variables.
# In Pry session
cd User # Navigate into User class
ls # List methods and constants
cd @current_user # Navigate into instance
ls -i # List instance variables
Code display commands show source definitions and documentation. The show-method
command reveals method implementations, while show-doc
displays method documentation. These commands accept method names, including those with special characters.
# Display method source
show-method User#authenticate
show-method User.find_by_email
# Show documentation
show-doc Array#map
show-doc String#gsub
Variable inspection works through direct evaluation and specialized commands. Typing a variable name evaluates and displays its value. The whereami
command shows the current execution location with surrounding code context.
def complex_calculation(data)
intermediate_result = data.map { |x| x * 2 }
binding.pry
# At this point:
# data # Shows original data
# intermediate_result # Shows mapped data
# whereami # Shows current line with context
end
Session control uses exit
to leave the current Pry session and continue execution. The exit-all
command terminates all nested Pry sessions. The next
and step
commands provide basic debugging navigation when using pry-byebug.
History access through the hist
command shows previously executed commands. The edit
command opens methods or classes in the configured editor. Shell commands execute by prefixing with periods or using the shell-mode
command.
Advanced Usage
Pry configuration customizes behavior through the Pry.config
object and ~/.pryrc
configuration file. The configuration system accepts options for prompt display, editor selection, command aliases, and output formatting.
# ~/.pryrc configuration
Pry.config.editor = 'vim'
Pry.config.auto_indent = true
Pry.config.correct_indent = true
Pry.config.collision_warning = true
# Custom prompt
Pry.config.prompt = Pry::Prompt.new(
:custom,
'Custom prompt',
[
proc { |obj, nest_level, _| "#{obj}:#{nest_level}> " },
proc { |obj, nest_level, _| "#{obj}:#{nest_level}* " }
]
)
Custom command creation extends Pry functionality through the Pry::Command
base class. Commands define their own parsing logic, help text, and execution behavior. The command registration system makes these commands available in all Pry sessions.
Pry::Commands.create_command 'find-routes' do
description 'Find Rails routes matching a pattern'
def process
pattern = args.first
Rails.application.routes.routes.select do |route|
route.path.spec.to_s.include?(pattern)
end.each do |route|
output.puts "#{route.verb} #{route.path.spec}"
end
end
end
Plugin integration happens through Pry's hook system and gem loading. Popular plugins like pry-byebug add debugging capabilities, while pry-rails integrates with Rails applications. Plugins register through the Pry.plugins
registry.
# Gemfile integration
gem 'pry-byebug'
gem 'pry-rails'
gem 'pry-remote'
# Plugin activation in .pryrc
begin
require 'pry-byebug'
Pry.commands.alias_command 'c', 'continue'
Pry.commands.alias_command 's', 'step'
rescue LoadError
puts "pry-byebug not available"
end
Binding manipulation allows navigation between different execution contexts. The cd
command changes the current object context, while nesting
shows the current navigation stack. Complex object inspection uses these navigation capabilities.
class ComplexService
def initialize(config)
@config = config
@processors = create_processors
binding.pry
end
private
def create_processors
# In Pry: cd @config, ls, cd .., cd @processors[0]
[]
end
end
Output customization controls how Pry displays evaluation results. The Pry.config.print
option accepts custom print functions that format output. Color schemes and syntax highlighting customize through the coderay
configuration.
# Custom output formatting
Pry.config.print = proc do |output, value, _pry_|
output.puts "=> #{value.inspect}"
output.puts " (#{value.class})"
end
# Syntax highlighting
Pry.config.color = true
Pry.config.theme = :solarized
Error Handling & Debugging
Exception handling in Pry sessions captures errors during evaluation and provides detailed stack traces. When expressions raise exceptions, Pry displays the error message, backtrace, and maintains the current binding context for inspection.
def risky_operation(data)
binding.pry
# Type: data.unknown_method
# Pry shows: NoMethodError and maintains context
# Variables remain accessible for inspection
end
The wtf?
command provides detailed exception information for the last raised error. This includes full backtraces with source locations and variable states at each frame. The command accepts numeric arguments to show different levels of detail.
# After an exception occurs
wtf? # Basic exception info
wtf? 5 # Show 5 stack frames
wtf?! # Extended debugging information
Debugging integration with pry-byebug adds step-through debugging capabilities. The step
, next
, continue
, and finish
commands control execution flow. Breakpoint management uses break
to set breakpoints and info breakpoints
to list them.
require 'pry-byebug'
def complex_algorithm(input)
binding.pry
# Available commands:
# step - Step into method calls
# next - Move to next line
# finish - Execute until return
# break User#authenticate - Set breakpoint
end
Variable inspection during exceptions reveals the state at error points. Local variables, instance variables, and method parameters remain accessible through the Pry session. The caller
method shows the call stack without stopping execution.
def process_payment(amount, card)
begin
charge_card(amount, card)
rescue PaymentError => e
binding.pry # Inspect amount, card, e
# All variables available for debugging
end
end
Remote debugging capabilities through pry-remote enable debugging of running processes. The binding.remote_pry
call starts a remote Pry server that accepts connections from separate terminals.
require 'pry-remote'
def background_worker
loop do
binding.remote_pry # Connect via: pry-remote
process_job
end
end
Stack frame navigation allows movement between different levels of the call stack. The up
and down
commands move between frames, while frame
shows the current frame information. Each frame provides access to its local variable context.
def level_three
binding.pry
# up - Move to level_two's frame
# down - Move back to level_three's frame
# frame - Show current frame details
end
def level_two
level_three
end
def level_one
level_two
end
Testing Strategies
Test environment integration embeds Pry sessions within test suites for debugging failing tests. The binding.pry
placement in test methods provides access to test doubles, fixture data, and assertion contexts during test execution.
RSpec.describe UserService do
let(:user_data) { { name: 'John', email: 'john@example.com' } }
it 'creates user with valid data' do
service = UserService.new
binding.pry # Access to service, user_data, and test context
result = service.create_user(user_data)
expect(result).to be_successful
end
end
Mock and stub inspection during tests reveals the interaction between test doubles and code under test. Pry sessions provide access to mock expectations, method call counts, and received arguments.
describe PaymentProcessor do
it 'processes payment correctly' do
gateway = double('PaymentGateway')
allow(gateway).to receive(:charge).and_return(success: true)
processor = PaymentProcessor.new(gateway)
binding.pry # Inspect gateway mock setup
processor.process_payment(100)
# In Pry: gateway.received_messages shows interactions
end
end
Fixture data examination uses Pry to inspect database records, factory-generated objects, and test data setup. This reveals the actual state of test fixtures versus expected states.
feature 'User registration' do
let!(:existing_user) { create(:user, email: 'taken@example.com') }
scenario 'prevents duplicate email registration' do
visit new_user_path
binding.pry # Examine existing_user state and page content
fill_in 'Email', with: 'taken@example.com'
click_button 'Register'
expect(page).to have_content('Email already taken')
end
end
Conditional debugging activates Pry sessions only when specific test conditions occur. Environment variables or test metadata control when debugging sessions start, preventing interruption of normal test runs.
RSpec.describe ComplexCalculation do
it 'handles edge cases', :debug do
result = ComplexCalculation.perform(edge_case_data)
binding.pry if ENV['DEBUG'] || example.metadata[:debug]
expect(result).to meet_expectations
end
end
# Run with debugging: DEBUG=1 rspec spec/calculation_spec.rb
# Or tag-based: rspec --tag debug spec/calculation_spec.rb
Test helper integration provides debugging utilities available across test suites. Custom commands and shortcuts enhance the debugging experience within test contexts.
# spec/spec_helper.rb
RSpec.configure do |config|
config.before(:each) do |example|
if example.metadata[:debug]
Pry.config.input = $stdin
Pry.config.output = $stdout
end
end
end
# Custom debugging helper
def debug_test(label = nil)
puts "\n=== #{label} ===" if label
binding.pry
end
Production Patterns
Production debugging requires careful Pry integration that avoids disrupting normal application flow. Conditional breakpoints use environment variables or feature flags to enable debugging sessions only when explicitly activated.
class OrderProcessor
def process_order(order)
# Conditional debugging for production investigation
binding.pry if ENV['DEBUG_ORDER_PROCESSING'] == 'true'
validate_order(order)
calculate_totals(order)
charge_payment(order)
end
end
Remote debugging in production environments uses pry-remote for investigating running processes without stopping the entire application. The remote connection isolates debugging sessions from normal request processing.
require 'pry-remote' if Rails.env.production? && ENV['ENABLE_REMOTE_DEBUGGING']
class BackgroundJob
def perform
binding.remote_pry if should_debug?
execute_job_logic
rescue => e
binding.remote_pry if critical_error?(e)
raise
end
private
def should_debug?
ENV['DEBUG_JOBS'] && job_id_matches_debug_pattern?
end
end
Rails integration through pry-rails enhances console sessions with Rails-specific functionality. The gem adds Rails route inspection, model introspection, and application configuration access within Pry sessions.
# In Rails console with pry-rails
rails console
# Enhanced Rails functionality
show-routes # Display all routes
show-routes users # Filter routes by pattern
show-models # List all models
reload! # Reload application code
Logging integration captures Pry session activity for audit trails and debugging history. Custom logging configurations record commands executed, variables inspected, and session duration.
# Custom Pry logging configuration
Pry.config.hooks.add_hook(:before_session, :logging) do |output, binding, pry|
Rails.logger.info("Pry session started in #{binding.eval('self.class')}")
end
Pry.config.hooks.add_hook(:after_session, :logging) do |output, binding, pry|
Rails.logger.info("Pry session ended")
end
Memory and performance monitoring during Pry sessions prevents resource exhaustion in production environments. Memory usage tracking and session timeouts protect against excessive resource consumption.
class ProductionSafePry
def self.safe_pry(binding_context)
return unless debugging_enabled?
Timeout::timeout(300) do # 5-minute session limit
binding_context.pry
end
rescue Timeout::Error
Rails.logger.warn("Pry session timeout - automatically terminated")
end
private
def self.debugging_enabled?
ENV['PRODUCTION_DEBUG'] == 'true' &&
current_memory_usage < memory_threshold
end
end
Security considerations protect sensitive data during production debugging. Data sanitization and access controls prevent exposure of credentials, personal information, and sensitive application state.
class SecurePrySession
def self.sanitize_binding(binding_context)
# Remove sensitive variables before Pry session
eval("remove_instance_variable(:@password)", binding_context) rescue nil
eval("remove_instance_variable(:@api_key)", binding_context) rescue nil
binding_context.pry
end
end
Reference
Core Commands
Command | Parameters | Returns | Description |
---|---|---|---|
cd <object> |
Object reference | nil |
Navigate into object context |
ls [options] |
-i , -m , -c , -g flags |
Array | List methods, variables, constants |
whereami [lines] |
Number of context lines | nil |
Show current code location |
edit <method> |
Method name or class | nil |
Open method/class in editor |
show-method <method> |
Method name, -l for line numbers |
nil |
Display method source code |
show-doc <method> |
Method name | nil |
Display method documentation |
hist [options] |
--head , --tail , --grep |
nil |
Show command history |
wtf? [depth] |
Number of frames to show | nil |
Display exception information |
exit |
None | nil |
Exit current Pry session |
exit-all |
None | nil |
Exit all nested Pry sessions |
Configuration Options
Option | Type | Default | Description |
---|---|---|---|
Pry.config.editor |
String | ENV['EDITOR'] |
Editor command for code editing |
Pry.config.auto_indent |
Boolean | true |
Automatic indentation |
Pry.config.color |
Boolean | true |
Syntax highlighting |
Pry.config.pager |
Boolean | true |
Paginate long output |
Pry.config.collision_warning |
Boolean | true |
Warn about command conflicts |
Pry.config.correct_indent |
Boolean | true |
Correct indentation mistakes |
Pry.config.prompt |
Pry::Prompt | Default prompt | Custom prompt configuration |
Debugging Commands (pry-byebug)
Command | Parameters | Returns | Description |
---|---|---|---|
step [lines] |
Number of steps | nil |
Step into method calls |
next [lines] |
Number of lines | nil |
Move to next line |
finish |
None | nil |
Execute until method return |
continue |
None | nil |
Continue execution |
break <location> |
Method or line reference | nil |
Set breakpoint |
info breakpoints |
None | Array | List all breakpoints |
delete <number> |
Breakpoint number | nil |
Delete specific breakpoint |
up [frames] |
Number of frames | nil |
Move up call stack |
down [frames] |
Number of frames | nil |
Move down call stack |
frame |
None | nil |
Show current frame information |
Hook Types
Hook | Trigger | Parameters | Description |
---|---|---|---|
:before_session |
Session start | output , binding , pry |
Execute before Pry starts |
:after_session |
Session end | output , binding , pry |
Execute after Pry exits |
:when_started |
First session | binding , pry |
Execute on first Pry invocation |
:before_eval |
Before evaluation | code , pry |
Execute before code evaluation |
:after_eval |
After evaluation | result , pry |
Execute after code evaluation |
Common Error Types
Error | Cause | Resolution |
---|---|---|
LoadError |
Missing gem dependencies | Install required gems |
NoMethodError |
Invalid command or method | Check command spelling and availability |
SyntaxError |
Invalid Ruby syntax | Correct syntax errors |
Pry::CommandError |
Invalid command usage | Check command help with help <command> |
SystemStackError |
Infinite recursion | Avoid recursive Pry calls |
Plugin Integration
Plugin | Gem | Key Features |
---|---|---|
pry-byebug | gem 'pry-byebug' |
Step debugging, breakpoints |
pry-rails | gem 'pry-rails' |
Rails console enhancement |
pry-remote | gem 'pry-remote' |
Remote debugging sessions |
pry-doc | gem 'pry-doc' |
Core Ruby documentation |
pry-theme | gem 'pry-theme' |
Color theme customization |