puts / p (Basic Output)
Ruby's standard output methods. puts outputs strings, p outputs inspect method results of objects. Used as simplest debugging means for temporary log output in learning stages and simple scripts. Inappropriate for full-scale logging.
Library
puts/p Methods
Overview
The puts/p methods are simple yet powerful debugging and logging techniques built into Ruby as standard. The puts method outputs the to_s representation of objects, while the p method calls the inspect method to display more detailed object information. Especially the p method is an essential debugging tool for Ruby developers, allowing instant inspection of instance variables, class names, and object IDs with comprehensive information during development debugging sessions.
Details
The puts/p methods are the most simple and reliable debugging techniques at the core of Ruby language with over 20 years of proven track record. puts is primarily used for strings and basic output, while the p method is designed to examine the internal state of objects in detail. The p method functions as a shortcut combining inspect method and puts, clearly distinguishing nil values, empty strings, and empty hashes to prevent oversight during debugging. It features IDE integration, no external library requirements, and immediate execution, maximizing development efficiency.
Key Features
- Built-in Standard Library: Included in Ruby without additional gem installation
- Detailed Object Display: p method simultaneously shows instance variables and class information
- Immediate Execution: Instant debugging information without compilation needed
- nil/Empty Value Distinction: Clearly identifies empty strings, nil, and empty hashes
- Lightweight Implementation: Extremely small overhead with high-speed operation
- Framework Independent: Available in any framework like Rails, Sinatra, etc.
Pros and Cons
Pros
- Built into Ruby as standard, immediately available without external dependencies
- Efficient debugging with detailed internal object state display through p method
- Almost zero learning cost, usable from Ruby beginners to advanced developers
- Extremely small runtime overhead with no performance impact
- Consistent operation across any development environment regardless of IDE or editor
- Perfect for quick verification in small implementation cycles during test-driven development
Cons
- No log level control functionality, requiring manual removal in production
- Impossible to customize output destination settings or formatting
- Inappropriate for structured log management in large-scale applications
- Risk of debug puts/p statements leaking into production in team development
- No advanced features like log persistence, searching, or analysis
- Difficult to manage complex debugging sessions across multiple files
Reference Pages
- Ruby Official Documentation - puts
- Ruby Official Documentation - p
- Ruby Official Documentation - inspect
Code Examples
Basic Setup
# No additional installation or require statements needed
# Immediately available if Ruby is installed
# Available at the beginning of Ruby scripts, IRB/Pry, or Rails console
puts "Hello, Ruby Logging!"
p "Hello, Ruby Debugging!"
Basic Log Output and Debugging
# puts method - basic string output
puts "Application started"
puts "Authenticating user..."
puts "Database connection complete"
# Display variable contents with puts
name = "John Doe"
age = 30
puts "Username: #{name}"
puts "Age: #{age}"
# p method - detailed object information display
user_data = { name: "John Doe", age: 30, email: "[email protected]" }
p user_data
# => {:name=>"John Doe", :age=>30, :email=>"john@example.com"}
# Clearly display differences between nil, empty string, empty array
p nil # => nil
p "" # => ""
p [] # => []
p {} # => {}
puts nil # => (empty line output)
puts "" # => (empty line output)
puts [] # => (empty line output)
# Detailed display of arrays and hashes
users = [
{ id: 1, name: "John Doe", role: "admin" },
{ id: 2, name: "Jane Smith", role: "user" }
]
puts "User array:"
puts users # => Each element's to_s representation displayed
puts "\nUser array (detailed):"
p users # => Complete object structure displayed
# Detailed display including object class information
class User
attr_accessor :name, :email, :created_at
def initialize(name, email)
@name = name
@email = email
@created_at = Time.now
end
end
user = User.new("Mike Johnson", "[email protected]")
puts user # => #<User:0x00007f8b1c0a1d20>
p user # => #<User:0x00007f8b1c0a1d20 @name="Mike Johnson", @email="mike@example.com", @created_at=2025-01-01 12:00:00 +0900>
Advanced Debugging Techniques
# Debug multiple values simultaneously
def calculate_total(items)
puts "=== calculate_total debug start ==="
p "Input items: #{items}"
total = 0
items.each_with_index do |item, index|
puts "Processing: #{index + 1}/#{items.length}"
p "Current item: #{item}"
if item.respond_to?(:price)
total += item.price
p "Total after price addition: #{total}"
else
puts "Warning: #{item} does not have price information"
p "Item type: #{item.class}"
p "Item methods: #{item.methods.sort}"
end
end
puts "=== Final result ==="
p "Total amount: #{total}"
puts "=== calculate_total debug end ==="
total
end
# Sample data for debugging
Product = Struct.new(:name, :price)
items = [
Product.new("Laptop", 800),
Product.new("Mouse", 20),
"Invalid data" # Intentionally problematic data
]
result = calculate_total(items)
# Debugging conditional branches
def process_user(user)
puts "User processing started"
p "User object: #{user}"
case user[:status]
when "active"
puts "Processing active user"
p "Last login: #{user[:last_login]}"
when "inactive"
puts "Processing inactive user"
p "Inactive reason: #{user[:inactive_reason]}"
when nil
puts "Status not set"
p "Complete user data: #{user}"
else
puts "Unknown status"
p "Received status: #{user[:status]}"
p "Expected statuses: ['active', 'inactive']"
end
end
# Debug execution with test data
test_users = [
{ name: "John Doe", status: "active", last_login: Time.now - 3600 },
{ name: "Jane Smith", status: "inactive", inactive_reason: "resigned" },
{ name: "Mike Johnson", status: nil },
{ name: "Bob Wilson", status: "suspended" }
]
test_users.each { |user| process_user(user) }
Error Handling and Debugging
# Debugging in exception handling
def safe_divide(a, b)
puts "Division operation started"
p "Dividend: #{a}, Divisor: #{b}"
begin
result = a / b
puts "Division successful"
p "Result: #{result}"
result
rescue ZeroDivisionError => e
puts "Zero division error occurred"
p "Error message: #{e.message}"
p "Error class: #{e.class}"
p "Backtrace: #{e.backtrace.first(3)}"
nil
rescue StandardError => e
puts "Unexpected error occurred"
p "Error object: #{e}"
p "Error message: #{e.message}"
p "Argument types: a=#{a.class}, b=#{b.class}"
raise # Re-raise the error
end
end
# Debug execution examples
test_cases = [
[10, 2], # Normal case
[10, 0], # Zero division error
["10", "2"], # Strings (potential TypeError)
[10, nil] # nil (potential TypeError)
]
test_cases.each do |a, b|
puts "\n" + "=" * 40
puts "Test case: #{a} ÷ #{b}"
result = safe_divide(a, b)
p "Final result: #{result}"
end
# Debugging nested data structures
def analyze_nested_data(data)
puts "Nested data analysis started"
p "Data structure: #{data}"
if data.is_a?(Hash)
puts "Hash data analysis"
data.each do |key, value|
puts "Key: #{key}"
p "Value: #{value}"
p "Value class: #{value.class}"
if value.is_a?(Array)
puts " Array element analysis:"
value.each_with_index do |item, index|
puts " Index #{index}:"
p " Value: #{item}"
p " Class: #{item.class}"
end
elsif value.is_a?(Hash)
puts " Nested hash analysis:"
value.each do |nested_key, nested_value|
puts " Nested key: #{nested_key}"
p " Nested value: #{nested_value}"
end
end
end
else
puts "Non-hash data"
p "Data type: #{data.class}"
p "Data content: #{data}"
end
end
# Test complex nested data
complex_data = {
user: {
id: 1,
profile: {
name: "John Doe",
settings: ["theme:dark", "lang:en"]
}
},
posts: [
{ id: 1, title: "Post 1", tags: ["ruby", "programming"] },
{ id: 2, title: "Post 2", tags: [] }
],
metadata: nil
}
analyze_nested_data(complex_data)
Practical Examples (Rails/Web Application Integration)
# Rails controller debugging example
class UsersController < ApplicationController
def create
puts "User creation process started"
p "Received parameters: #{params}"
@user = User.new(user_params)
puts "New user object created"
p "User attributes: #{@user.attributes}"
p "Pre-validation state: valid=#{@user.valid?}"
if @user.errors.any?
puts "Validation errors present"
p "Error details: #{@user.errors.full_messages}"
end
if @user.save
puts "User save successful"
p "Saved user: #{@user}"
redirect_to @user, notice: 'User was created successfully'
else
puts "User save failed"
p "Error content: #{@user.errors.full_messages}"
render :new
end
end
private
def user_params
permitted = params.require(:user).permit(:name, :email, :age)
puts "Permitted parameters"
p permitted
permitted
end
end
# Rails model debugging example
class User < ApplicationRecord
before_save :debug_before_save
after_save :debug_after_save
validates :name, presence: true, length: { minimum: 2 }
validates :email, presence: true, uniqueness: true
private
def debug_before_save
puts "User#before_save callback"
p "Pre-save attributes: #{attributes}"
p "Changed attributes: #{changed_attributes}"
end
def debug_after_save
puts "User#after_save callback"
p "Post-save ID: #{id}"
p "Post-save attributes: #{attributes}"
end
end
# Background job debugging
class EmailSendJob < ApplicationJob
queue_as :default
def perform(user_id, email_type)
puts "EmailSendJob execution started"
p "User ID: #{user_id}, Email type: #{email_type}"
user = User.find(user_id)
puts "User retrieval completed"
p "User information: #{user}"
case email_type
when 'welcome'
puts "Welcome email sending process"
UserMailer.welcome_email(user).deliver_now
when 'reminder'
puts "Reminder email sending process"
UserMailer.reminder_email(user).deliver_now
else
puts "Unknown email type"
p "Received email type: #{email_type}"
raise ArgumentError, "Unknown email type: #{email_type}"
end
puts "EmailSendJob execution completed"
rescue => e
puts "Error occurred in EmailSendJob"
p "Error: #{e}"
p "Backtrace: #{e.backtrace.first(5)}"
raise
end
end