Sunday, October 23, 2011

Here documents in Ruby

I'm still working through Learn Ruby the Hard Way, and the last few exercises have been about building simple text based dungeons. You may recall I spent about a week working on one of my own. This latest exercise introduced me to here documents.

A here document is an easy way to display a long, multiline string. This would have been PERFECT for use in my dungeon adventure. Here's how it works.

This is the original description from the first room in the dungeon game I built:

  puts "This room is dimly lit by two sputtering torches."
  puts "There are three doors leading out of here."
  puts "What do you do?"

You may notice a few "puts" methods. It's not terrible in this case, since I'm putting several small statements, but it's tedious to type and unnecessary to use so many methods when you could do it with one here document. So forget what I said. There's a better, simpler way to do it, so it IS terrible!

So this example can be re-written like this:

  puts <<−TORCH_DESCRIPTION
  This room is dimly lit by two sputtering torches.
  There are three doors leading out of here.
  What do you do?
  TORCH_DESCRIPTION

You begin the here document with <<−TORCH_DESCRIPTION and end it with TORCH_DESCRIPTION. Ruby interprets everything in between as one string. Cleaner, right? Easier to read? Who wouldn't love it!

On a side note, you CAN write this as <<TORCH_DESCRIPTION instead, but by adding the minus sign, you can indent the code as usual. Without the minus sign, the terminator can't be indented, or else Ruby will start complaining about "can't find string TORCH_DESCRIPTION anywhere before EOF".

So I recommend using the minus sign. One more character makes your code a lot easier to read. I also ran into another little problem. Since a here document is a string literal, any variables won't be interpreted correctly. This is where interpolation comes into play. Here's an example from the current exercise I'm working through.

guess = gets.chomp()

if guess != good_pod
  puts "You jump into pod %s and hit the eject button." % guess
  puts "The pod escapes out into the void of space, then"
  puts "implodes as the hull ruptures, crushing your body"
  puts "into jam jelly."
return :death

This bit of code describes an escape pod room, where the user has to pick an escape pod to use. So we set the user's choice as "guess" and then call it in the method below. This is what happens when a user chooses poorly. However, if I were to rewrite the code using a here document, this is what I'd have:

  puts <<-BAD_POD
  "You jump into escape pod %s  and hit the eject button. %guess
  The pod escapes out into the void of space, then
  implodes as the hull ruptures, crushing your body
  into a jelly."
  BAD_POD
return :death

Now the %s and %guess won't be interpreted as variables, since they're inside of a string literal. Luckily, there's an easy solution: interpolation. Just swap out the %s for this little guy:

#{guess}

Ruby will look at this, understand that means you want to use a variable inside of a string literal, and then pull the information it collected when it asked the user which pod he wanted to jump into.

I'm hoping this post will be helpful for others who are grappling with here documents. It took me a bit of digging to figure out how to get the code to index properly (since it looked awful in Textmate, and my solution was to put the string in quotes, which was unnecessary) and I needed to figure out how to call a variable from inside the here document.

No comments:

Post a Comment