Question

Inspect and Pretty-Print Ruby Objects and Arrays in the Console

rubyprintf-debugging

Question

When running a simple Ruby script, what is the easiest way to display an object's fields in the console? I need an equivalent to PHP's print_r() that also works well with arrays.

Short Answer

Ruby provides several useful debugging tools for displaying values: p prints an inspectable representation, object.inspect returns that representation as a string, and pp pretty-prints nested arrays and hashes. For a custom object's stored instance variables, use instance_variables and instance_variable_get when you need to see its internal state explicitly.

Concept

Ruby does not have one exact equivalent of PHP's print_r(), because Ruby offers a few tools for different inspection needs.

  • puts displays a human-friendly string by calling to_s.
  • p displays a debugging representation by calling inspect.
  • inspect returns that debugging representation as a string.
  • pp (pretty print) formats nested data across multiple lines, making complex arrays and hashes easier to read.

For debugging, p and pp are usually better choices than puts. They preserve useful details such as quotation marks around strings, array brackets, hash keys, and escape characters.

Custom Ruby objects may display their class and instance variables automatically through inspect, but that output is not a guaranteed public API. If you specifically need the object's instance variables, Ruby provides reflection methods such as instance_variables and instance_variable_get.

These tools matter because developers constantly need to verify what data exists at a particular point in a program: API responses, configuration hashes, parsed files, loop values, and an object's current state.

Mental Model

Think of a Ruby value as an item you want to identify.

  • puts is like asking someone to describe the item in a pleasant sentence.
  • p is like asking for the item's diagnostic label, including technical details.
  • pp is like laying the contents of a large box neatly on a table so each part is easy to examine.

When debugging, the diagnostic label or neatly arranged contents are usually more useful than a friendly description.

Take Quiz

Syntax and Examples

Use p for a quick debug print:

name = "Ada"
p name
# "Ada"

p calls inspect, so these are closely related:

items = ["book", "pen", 3]

p items
puts items.inspect

Both display a representation similar to:

["book", "pen", 3]

Use pp for nested arrays and hashes. Load Ruby's standard pp library first:

require "pp"

order = {
  id: 42,
  customer: {
    name: "Ada",
    address: { city: "London", country: "UK" }
  },
  items: ["book", "pen"]
}

pp order

pp formats the nested structure over multiple lines when necessary, which is easier to scan than one very long line.

For a custom object:

class User
  def initialize(name, admin)
    @name = name
    @admin = admin
  end
end

user = User.new("Ada", true)
p user

A typical result is similar to:

#<User:0x0000... @name="Ada", @admin=true>

The hexadecimal object identifier changes between runs, so do not rely on it in program logic.

Step by Step Execution

Consider this script:

require "pp"

scores = [
  { player: "Ada", points: 10 },
  { player: "Lin", points: 15 }
]

p scores
pp scores

Execution flow:

  1. require "pp" loads the pp method from Ruby's standard library.
  2. Ruby creates an array containing two hashes and assigns it to scores.
  3. p scores calls scores.inspect and writes its returned string to standard output. The result is commonly compact and may appear on one line.
  4. pp scores recursively formats the array and its hashes. It adds line breaks and indentation when useful.
  5. Neither p nor pp changes scores; they only display it.

You can also inspect selected object fields explicitly:

class Product
  def initialize(name, price)
    @name = name
    @price = price
  end
end

product = Product.new("Notebook", 4.50)

product.instance_variables.each do |variable|
  value = product.instance_variable_get(variable)
  puts "#{variable}: #{value.inspect}"
end

This visits :@name and :@price, retrieves each stored value, and prints a readable line for each one.

Real World Use Cases

  • Inspecting API data: Use pp response_body after parsing JSON to understand a nested response before extracting values.
  • Debugging configuration: Print a hash of environment-specific settings to verify the values loaded by a script.
  • Checking loop data: Use p record inside a loop to find an unexpected record or missing field.
  • Investigating parsed files: Pretty-print CSV-derived hashes, YAML data, or JSON data while developing an import script.
  • Examining object state: Use p service or list instance variables when a class appears to hold the wrong values.
  • Writing quick command-line scripts: Print an array of results with pp before deciding how to format the final output for users.
Take Quiz

Real Codebase Usage

In production codebases, direct console printing is most common during local development, scripts, and temporary debugging. Developers generally choose the tool based on the purpose.

Quick local debugging

p params
p user_id

This is useful for a quick checkpoint because strings remain visibly quoted and arrays remain structured.

Readable nested diagnostics

require "pp"
pp payload

This is common when inspecting request payloads or deeply nested application data.

Logging selected, meaningful fields

Rather than dumping an entire object—which can be noisy or expose secrets—real applications often log a small hash:

logger.info(
  event: "payment_failed",
  order_id: order.id,
  error_class: error.class.name
)

Validation and guard clauses

When a value is unexpected, inspect it near the boundary where it enters the program:

unless payload.is_a?(Hash)
  p payload
  raise ArgumentError, "payload must be a Hash"
end

For long-running applications, use the project's logger instead of leaving p statements in code. Remove temporary debugging output or replace it with deliberate, safe logging.

Common Mistakes

Using puts when you need debugging detail

value = ["a", "b"]
puts value

This prints each array element on its own line:

a
b

Use p value or pp value when you need to see the array structure.

Expecting p to return the inspected string

text = p({ status: "ok" })

p prints the value and returns the original object, not the string from inspect. If you need a string, use inspect:

text = { status: "ok" }.inspect

Forgetting to load pp

pp data

In a normal Ruby script, this can fail because pp is not loaded automatically. Add:

require "pp"

Assuming every object's inspect output is complete

A class can define its own inspect method. Some objects intentionally hide internal details, and some objects contain a large amount of data. If needed, inspect known public methods or explicit instance variables instead.

Printing sensitive data

Avoid dumping passwords, access tokens, session data, or full customer records into a shared terminal or application log. Print only the fields needed to diagnose the problem.

Treating instance variables as a stable public interface

instance_variables is useful for debugging, but application code should normally use public methods such as user.name rather than reaching into @name directly.

Comparisons

ToolMain purposeOutput styleReturn valueBest use
puts valueUser-facing displayUses to_s; may lose structurenilSimple text output
p valueQuick debuggingUses inspectThe original valueOne-line inspection
value.inspectBuild a debug stringUses inspectA StringInterpolation, logging, custom output
pp valueRead nested dataPretty-formatted inspectionThe original valueArrays and hashes with nesting
object.instance_variablesFind stored instance variablesArray of symbolsAn arrayInspecting custom object state

p versus pp:

p data   # fastest quick check
pp data  # easier to read when data is deeply nested

Use inspect when you need the representation as data rather than immediate console output:

message = "Unexpected response: #{response.inspect}"
Take Quiz

Cheat Sheet

# Quick debugging output
p value

# Get a debugging representation as a String
text = value.inspect

# Pretty-print nested data
require "pp"
pp value

# List an object's instance variable names
object.instance_variables
# => [:@name, :@count]

# Read one instance variable dynamically
object.instance_variable_get(:@name)

Rules of thumb:

  • Use puts for simple user-facing output.
  • Use p for fast debugging.
  • Use pp for complex arrays and hashes.
  • Use inspect when you need a string.
  • Do not leave noisy debug prints or sensitive values in production logs.
  • Prefer public reader methods over instance-variable reflection in normal application logic.

FAQ

What is Ruby's equivalent of PHP print_r()?

For quick inspection, use p value. For more readable output of nested arrays and hashes, use require "pp" followed by pp value.

What is the difference between p and puts in Ruby?

puts uses to_s and aims for readable text. p uses inspect, which preserves debugging details such as quotes, brackets, and escape sequences.

Do I need to require anything to use p?

No. p is available by default. pp requires require "pp" in a standard Ruby script.

How do I print an array on one line in Ruby?

Use p array or puts array.inspect. puts array prints each element separately, which is often not what you want for debugging.

How do I see the fields of a Ruby object?

Start with p object. To list its instance variables, call object.instance_variables. To read one dynamically, call object.instance_variable_get(:@field_name).

Why does p object show a value like 0x0000...?

That is part of Ruby's default object inspection output and helps identify the object instance. It is not stable between program runs.

Can inspect be customized?

Yes. A class can define its own inspect method. This can make debugging output clearer, but it should avoid exposing secrets.

Take Quiz

Mini Project

Description

Create a small inventory-debugging script. It stores products as hashes inside an array, pretty-prints the full inventory, and then displays the instance variables of an InventoryReport object. This mirrors the kind of temporary diagnostic script used while checking imported data.

Goal

Print nested inventory data clearly and inspect the stored state of a custom Ruby object.

Requirements

  • Create an array containing at least two product hashes.
  • Give each product a name, price, and tags array.
  • Pretty-print the complete inventory.
  • Create an object that stores the inventory and a report name.
  • Print each instance variable name and its inspected value.
Take Quiz

Keep learning

Related questions

View more