Let's start with ...
The documentation says:
A here document or heredoc can be useful for writing strings spanning over multiple lines.
And continues explaining how they are defined:
A heredoc is denoted by
<<-followed by a heredoc identifier [...]. The heredoc starts in the following line and ends with the next line that contains only the heredoc identifier.
The following is an example on how to define a
<<-HERE_BE_DOC Hello World Beware, here be dragons! HERE_BE_DOC
heredoc (as any other
String) can be assigned to a variable:
dragons = <<-HERE_BE_DOC Hello World Beware, here be dragons! HERE_BE_DOC puts dragons
This example outputs:
Hello World Beware, here be dragons!
And of course, we can use them as a method's argument:
def string_length(str : String) str.size end puts string_length <<-HERE_BE_DOC Hello World Beware, here be dragons! HERE_BE_DOC
This example outputs:
Note: It keeps original newlines and spaces! 🤩
So, if a
heredoc is a way of defining a
String, then a
heredoc responds to all
String methods (because it is a String). The last example shows exactly this when using the method
So, let's try the following example:
"Hello World".size # => 11
but using a
puts <<-WORLD Hello World WORLD.size
Error: Unterminated heredoc: can't find "WORLD" anywhere before the end of file
Oh! It cannot find the end of the
heredoc (meaning the compiler "is reading"
WORLD.size as an entire word) 🤔 ... wait 🤓💡 let's fix it like this:
<<-WORLD Hello World WORLD .size # => 11
It worked 🤓🎉 but this solution feels more like a hack. Let's go to the documentation to find the correct (although not so intuitive) way of writing our example:
After the heredoc identifier, and in that same line, anything that follows continues the original expression that came before the heredoc. It's as if the end of the starting heredoc identifier is the end of the string.
So the following should work:
<<-WORLD.size # => 11 Hello World WORLD
Great! It's working!
(We should pin this as it might be helpful later. 📌)
As I was saying at the beginning of this post, I was writing some tests using the method
assert_format. Let's focus on the first two arguments of this method: the first argument is the input for the formatter and the second is the expected result after formatting the input.
Since we are formatting source code, using
heredoc would be really handy (because we can use indentation and newlines making the code to be formatted easy to read), except for one single detail: the incorrect way I was trying to use it 🙈
assert_format <<-BEFORE alias Foo= Bar BEFORE, <<-AFTER alias Foo = Bar AFTER
and the compiler was returning:
Error: Unterminated heredoc: can't find "BEFORE" anywhere before the end of file
This sounds familiar, right? Remember the
WORLD.size example? Well, we have the same problem here with
And the solution is the same described in the docs. We should pass both
heredocs to the method like this:
assert_format <<-BEFORE, <<-AFTER alias Foo= Bar BEFORE alias Foo = Bar AFTER
It's working! 🎉
The crux of the matter was in the first line
assert_format <<-BEFORE, <<-AFTER. Remember the docs:
It's as if the end of the starting heredoc identifier is the end of the string.
Finally, here is the same example but using
strings instead of
assert_format " alias Foo=\n Bar", " alias Foo = Bar"
It looks like we didn't gain much but here is a more complex example, with multiple
newlines and indentations.
We have learned about Heredoc, how we can use Heredoc's methods, and how to use multiple
heredocs as arguments to a method.
All the above and no dragons were harmed while writing this post! 😁
Hope you enjoyed it! 😃