— Another minor-but-useful feature of Ruby—heredocs—is demonstrated — 3-minute read

Ruby definitely did not invent heredocs (Wikipedia says they originated with Unix shells), but they are one more of those little things that I really appreciate in Ruby.

At their simplest, they are merely a means of defining a (potentially) multi-line string, thus:

def lorem << LOREM Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut ... LOREM end p lorem #-> "Lorem ipsum dolor sit amet,..."

That odd-looking <<LOREM syntax introduces LOREM as the delimiter for the heredoc, and the parser then reads all subsequent lines of text up to (but not including) the final LOREM and treats them as a single string, including all whitespace. (You don’t have to use the word LOREM , either–any valid identifier will do.)

With this syntax, the final delimiter (e.g. LOREM ) must appear on a line by itself with no leading whitespace, which can be kind of a bummer sometimes. (It tends to put a real cramp in one’s neatly indented program.) Fortunately, there’s a solution for that: prefix the first delimiter with a hyphen, and Ruby will ignore whitespace in front of the final delimiter.

def lorem <<- LOREM Lorem ipsum dolor sit amet, consectetur adipiscing quis nostrud exercitation ullamco laboris nisi ut ... LOREM end p lorem #-> " Lorem ipsum dolor sit amet,..."

This lets you indent the final delimiter…but remember that all whitespace is preserved inside the heredoc. If you indent the text inside the heredoc, those indentations will be in the final string. Sometimes you want that…other times, not so much.

This leads to a quirky (but oh, so useful!) little thing about heredocs: syntactically, the first delimiter represents the entire heredoc. That is to say, if you want to invoke a method on the string, you attach the method invocation to the first delimiter, not the last! Like this:

def lorem <<- LOREM . gsub ( /^\s*/ , "" ) Lorem ipsum dolor sit amet, consectetur adipiscing quis nostrud exercitation ullamco laboris nisi ut ... LOREM end p lorem #-> "Lorem ipsum dolor sit amet,..."

Note how the gsub call is attached to the first delimiter. This will call gsub on the entire heredoc, stripping leading whitespace from each line of the string.

Quirky? Yes, perhaps, but really handy. Because that first delimiter can stand in for the entire heredoc, it lets you pass entire heredocs as arguments to methods:

def lorem_sub ( original ) original . gsub ( /secret text/ , <<- LOREM . gsub ( /^\s*/ , "" )) Lorem ipsum dolor sit amet, consectetur adipiscing quis nostrud exercitation ullamco laboris nisi ut ... LOREM end p lorem_sub ( "Beware the secret text in this string!" ) #-> "Beware the Lorem ipsum dolor sit amet...in this string!"

See that? The first delimiter just drops right in as an argument, and the heredoc picks right up on the next line. You can even (though I don’t recommend it) pass multiple heredocs to a single method invocation. (Pardon the contrived example here…this really is something you’ll never want to do, but it’s fun to know that it can be done!)

def replace ( original , pattern , replacement ) original . gsub ( pattern , replacement ) end result = replace ( << ORIGINAL , /secret text/ , << REPLACEMENT ) Beware the secret text in this really long multiline string! The secret text is something that should be kept secret. ORIGINAL Lorem ipsum dolor sit amet, consectetur adipiscing quis nostrud exercitation ullamco laboris nisi ut ... REPLACEMENT p result #-> "Beware the Lorem ipsum dolor..."

Also, it’s worth noting that by default, heredocs in Ruby are subject to string interpolation, just as if they were double-quoted strings:

address = << ADDRESS . gsub ( /^\s*/ , "" ) #{first_name} #{last_name} #{street_address1} #{street_address2} #{city}, #{state} #{zip} ADDRESS

If you don’t want a heredoc to be string-interpolated (that is, you want to treat it like a single-quoted string), just single-quote the first delimiter:

template_string = <<- ' TEMPLATE ' My name is #{first_name}. I like #{favorite_food}. TEMPLATE p template_string #-> " My name is \#{first_name}..."

You can use double-quotes, too, to get the default behavior (if you like being explicit). And if you use backticks, Ruby will execute each line in a shell and replace it with the output of that command:

output = <<- ` COMMANDS ` echo first echo second COMMANDS p output #-> "first

second

"

Heredocs may not be a groundbreaking, game-changing feature of Ruby, but they certainly make me smile when I need them. Little things, I tell you!

(I’m sure there have got to be more tricks involving heredocs, ones I’ve never seen, so if you happen to have a favorite, please share it!)