Kernel#pp
pp(*objects) -> objects pp is a Kernel method that outputs one or more objects to standard output using a pretty-printed format. Unlike puts or p, it uses the PP library to format complex data structures in a readable, nested way that reveals structure without overwhelming output.
Syntax
pp object
pp object1, object2, object3
pp *array
pp accepts one or more objects and prints each on its own line in a structured format. Passing an array with the splat operator expands it into individual arguments, so each element gets its own indented representation. The method returns the array of objects it received, making it chainable in debugging pipelines.
Parameters
| Parameter | Type | Description |
|---|---|---|
*objects | Object(s) | One or more objects to output. Each object is pretty-printed on its own line. |
The variadic signature means pp handles a single value, multiple values, or a splatted array with the same clean call. Each argument is output on its own line, which keeps multi-object dumps readable even when the objects are large or nested. The variadic design also means you can splat an array directly into the call without writing a loop, which keeps ad hoc debugging scripts concise and easy to type at the console.
Examples
Basic Usage
require 'pp'
pp "Hello, World!"
# => "Hello, World!"
pp 42
# => 42
Simple values like strings and numbers print similarly to p, but the value of pp becomes clearer when the objects have structure. The method handles nested data gracefully by adding indentation at each level of nesting, which is why the examples below focus on arrays and hashes where the visual benefit is most obvious.
Pretty-printing arrays
require 'pp'
arr = [1, 2, 3, 4, 5]
pp arr
# => [1, 2, 3, 4, 5]
nested = [1, [2, 3], [[4, 5]]]
pp nested
# => [1, [2, 3], [[4, 5]]]
The nested array example shows where pp starts to pull ahead of p. A deeply nested structure printed by p would appear as one long line, while pp preserves the nesting visually with indentation at each level. This becomes more valuable as the data gains additional layers of nesting.
Pretty-printing hashes
require 'pp'
hash = { name: "Alice", age: 30, city: "London" }
pp hash
# => {:name => "Alice", :age => 30, :city => "London"}
deep_hash = { user: { profile: { name: "Bob", settings: { theme: "dark" } } } }
pp deep_hash
# => {:user => {:profile => {:name => "Bob", :settings => {:theme => "dark"}}}}
With a deep hash, pp produces indented output that mirrors the data’s actual structure. Each level of nesting gets its own indentation level, which makes it easy to see which keys belong to which parent. This is especially helpful when debugging API responses or configuration objects that are several levels deep.
Inspecting objects
require 'pp'
class Person
def initialize(name, age)
@name = name
@age = age
end
end
pp Person.new("Charlie", 25)
# => #<Person:0x00007f8a2c3d4020 @name=\"Charlie\", @age=25>
For custom objects, pp shows the class name, object ID, and instance variable values in a readable format. This provides enough detail to understand an object’s internal state during debugging without writing a custom inspect method. When instance variables hold nested data, pp adds line breaks to keep the output scannable.
Common Patterns
Debugging data structures
require 'pp'
def process_request(params)
pp params
# Output clearly shows nested structure
result = transform(params)
pp result
result
end
Inserting pp calls before and after a transformation gives you a before-and-after view of the data without changing the method’s return value. The method still returns result at the end, so the debugging output does not interfere with the call site. This makes pp a drop-in inspection tool during development.
Comparing output methods
require 'pp'
data = { users: [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }] }
puts data
# {:users=>[{:id=>1, :name=>\"Alice\"}, {:id=>2, :name=>\"Bob\"}]}
p data
# {:users=>[{:id=>1, :name=>\"Alice\"}, {:id=>2, :name=>\"Bob\"}]}
pp data
# {:users =>
# [{:id => 1, :name => \"Alice\"},
# {:id => 2, :name => \"Bob\"}]}
The comparison above shows why pp is the right choice for structured data. puts and p collapse nested structures into a single dense line, while pp adds indentation that mirrors the data’s actual shape. For any hash or array deeper than one level, the visual difference is immediately obvious and saves time during debugging.
Pretty-printing large arrays
require 'pp'
large_array = (1..20).to_a
pp large_array
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
For larger arrays, pp wraps the output across multiple lines when it exceeds the configured width, keeping the data readable without horizontal scrolling. The line breaks are chosen to balance readability with compactness, which is useful when inspecting collections in a terminal or log file.
pp vs p vs puts
| Method | Format | Return Value | Best For |
|---|---|---|---|
puts | to_s | nil | User-facing output |
p | inspect | The object | Quick debugging |
pp | PrettyPrint | The object | Complex data structures |
When pretty printing helps
pp is the best choice when the shape of the data matters more than the raw value. Nested arrays, hashes, and custom objects become much easier to read when they are formatted with indentation and line breaks. That makes it especially useful in debugging sessions, test output, and ad hoc scripts where you want to inspect the structure before deciding what to do next. It is less suited to user-facing messages, where a shorter and more controlled format is usually better.
The method also makes diffs easier to scan when the data changes over time. A prettified dump can show which branch of a nested structure moved or changed without turning the output into a single long line. That is helpful when you are comparing fixtures, inspecting API responses, or checking whether a transformation kept the shape you expected.
When you use pp, try to keep the surrounding explanation short and direct. The whole point is to make the object easier to understand, so the message around it should not bury the useful output. In tests and diagnostics, a clean pretty-printed dump often gives you enough detail to spot the issue without adding more logging at all.
pp is a companion to other debug output, not a replacement for clear naming or well-shaped data. If the object is already hard to understand before it is printed, pp can reveal that shape, but the code still needs to explain why the structure matters. It works best as a helper during investigation, not as the only source of truth.
require 'pp'
# Inspect nested configuration at a glance
config = { database: { host: "localhost", port: 5432, pool: 5 } }
pp config
# {:database=>{:host=>"localhost", :port=>5432, :pool=>5}}