In Progress
Unit 1, Lesson 1
In Progress

p

Do you ever use puts for debugging? Don’t be shy, I do it too. Even Aaron Patterson uses puts to debug. It’s simple, easy to remember, and always available.

But as a debugging aid, puts has some drawbacks as well.

In this episode, you’ll learn about some of those limitations. And then you’ll learn about an alternative that’s also simple, also easy to remember, also always available… and is specifically included in Ruby to make adding debug output quicker, safer, and more revealing than with puts. Enjoy!

Video transcript & code

Everyone uses puts for debugging at some point. It's simple, it's quick, and it's reliable—which can't always be said for Ruby's debugger.

But puts output isn't always the most helpful for debugging. Let's take a look at some examples:

require 'nokogiri'

str   = "<p>Hello, world</p>"
html  = Nokogiri::HTML.fragment('<p>Hello, world</p>')
blank = ""
a_nil = nil
sym   = :foo
arr   = [:foo, :bar, :baz]
puts str
puts html
puts blank
puts nil
puts sym
puts arr
# >> <p>Hello, world</p>
# >> <p>Hello, world</p>
# >> 
# >> 
# >> foo
# >> foo
# >> bar
# >> baz

The string and the Nokogiri HTML fragment come out looking identical, even though they are very different objects. Likewise with an empty string and nil: both simply print as nothing at all, which isn't very useful. The symbol is indistinguishable from a string. And passing an array to puts results in each item being printed on a separate line. There's no way to tell where printing of individual items stops and the array begins.

We can improve the output by appending .inspect  to every value.

require 'nokogiri'

str   = "<p>Hello, world</p>"
html  = Nokogiri::HTML.fragment('<p>Hello, world</p>')
blank = ""
a_nil = nil
sym   = :foo
arr   = [:foo, :bar, :baz]
puts str.inspect
puts html.inspect
puts blank.inspect
puts nil.inspect
puts sym.inspect
puts arr.inspect
# >> "<p>Hello, world</p>"
# >> #<Nokogiri::HTML::DocumentFragment:0x11ca95c name="#document-fragment" children=[#<Nokogiri::XML::Element:0x11ca6a0 name="p" children=[#<Nokogiri::XML::Text:0x11c9ffc "Hello, world">]>]>
# >> ""
# >> nil
# >> :foo
# >> [:foo, :bar, :baz]

Now the string is clearly distinct from the DOM fragment; the nil and the empty string are unambiguously represented; the symbol is obviously a symbol; and the array prints out in familiar array notation.

But appending .inspect is tedious and easy to forget. Fortunately, Ruby gives us a tool for just this purpose: the p method. Let's get rid of the inspect calls and replace puts with p.

require 'nokogiri'

str   = "<p>Hello, world</p>"
html  = Nokogiri::HTML.fragment('<p>Hello, world</p>')
blank = ""
a_nil = nil
sym   = :foo
arr   = [:foo, :bar, :baz]
p str
p html
p blank
p nil
p sym
p arr
# >> "<p>Hello, world</p>"
# >> #<Nokogiri::HTML::DocumentFragment:0xad708c name="#document-fragment" children=[#<Nokogiri::XML::Element:0xada0e8 name="p" children=[#<Nokogiri::XML::Text:0xadc910 "Hello, world">]>]>
# >> ""
# >> nil
# >> :foo
# >> [:foo, :bar, :baz]

As you can see, p  is equivalent to the combination of puts and inspect, but it's a lot quicker to type than either of them.

We can even pass multiple arguments to p to have each one inspected on its own line:

require 'nokogiri'

str   = "<p>Hello, world</p>"
html  = Nokogiri::HTML.fragment('<p>Hello, world</p>')
blank = ""
a_nil = nil
sym   = :foo
arr   = [:foo, :bar, :baz]
p str, html, blank, a_nil, sym, arr
# >> baz  
# >> "<p>Hello, world</p>"
# >> #<Nokogiri::HTML::DocumentFragment:0x164e104 name="#document-fragment" children=[#<Nokogiri::XML::Element:0x164dc04 name="p" children=[#<Nokogiri::XML::Text:0x164d5c4 "Hello, world">]>]>
# >> ""
# >> nil
# >> :foo
# >> [:foo, :bar, :baz]

p has one more advantage over puts: it returns the first value passed to it. To see what this means, let's compare puts with p:

puts "Hello"                    # => nil
p "Hello"                       # => "Hello"
# >> Hello
# >> "Hello"

As you can see, puts returned nil, p returned the string I passed to it.

Why is this useful? Well, let's say we're debugging some code that looks for palindromes:

palindromes = ["racecar","banana","never odd or even"].select{|phrase|
  phrase.reverse == phrase
}
puts "Results: #{palindromes}"
# >> Results: ["racecar"]

Confused as to why the phrase "never odd or even" doesn't show up in the results, we might try and use puts to see the values of the reversed words:

palindromes = ["racecar","banana","never odd or even"].select{|phrase|
  puts(phrase.reverse) == phrase
}
puts "Results: #{palindromes}"
# >> racecar
# >> ananab
# >> neve ro ddo reven
# >> Results: []

But this alters the behavior of the code under test, something we try to avoid when debugging! In this case it just alters the output, but in other cases the nil return from puts might have caused a NoMethodError to be raised.

If, instead, we use p, the behavior of the code remains the same, because the value we give to p is also its return value.

palindromes = ["racecar", "banana", "never odd or even"].select{|phrase|
  p(phrase.reverse) == phrase
}
puts "Results: #{palindromes}"
# >> "racecar"
# >> "ananab"
# >> "neve ro ddo reven"
# >> Results: ["racecar"]

This makes p a better choice for inserting debugging output inline.

So next time you reach for puts to help you debug, use p  instead. Happy hacking!

Responses