Heredocs and how to use them in Ruby and Rails


What is heredoc?

Heredoc allows us to specify a string as a block of texts where new lines and indentations are maintained.

This is useful in Ruby for various cases like specifying multiline text strings, multiline method dynamic definitions and more.

There are many ways to use Heredoc in Ruby and in Rails, with different utilities. This can be confusing at times where to use which version. Let’s do a run-down of different ways to use it.

How to define?

We can define multiline strings in Ruby in the following ways:

> str = <<HEREDOC
>  This is
>  a sample
>  text.
>HEREDOC
=> "  This is\n  a sample\n  text.\n"
> str = <<-HEREDOC
>  This is
>  a sample
>  text.
> HEREDOC
=> "  This is\n  a sample\n  text.\n"

Here HEREDOC is the start and end marker. It can be any string.

This can be used in embedding the code snippets like SQL statements or HTML. For Example:

> query = <<-SQL
> Select * from users
> where name = 'John'
> SQL
=> "Select * from users\nwhere name = 'John'\n"

There is a very subtle difference between << and <<-. << expects end marker(HEREDOC in our case) should be at the start of the line. Whitespace before the marker at the end of the line is invalid syntax.

We can use <<- instead if we need to add more spaces for better reading of the text.

> str = <<-HEREDOC
>  Notice the spaces immediately before
>  the end.
>          HEREDOC
=> "Notice the space immediately before\nthe end.\n"

Input Redirection

<< can be used for input redirection in Ruby. Below is an example of heredoc with input redirection.

> File.open('test.rb', 'w') do |f|
>  f << <<-HEREDOC
>    This is a test
>    File
>  HEREDOC
end

Now let’s read the content of the test.rb file.

> f = File.open('test.rb', 'r')
> f.read
=> "   This is a test\n   File\n"

We can also use %Q instead of heredoc. The following example will add a new line at the start and end of the string.

>str = %Q(
>  This is
>  a sample
>  text.
>)
=> "\nThis is\na sample\ntext.\n"

Interpolation

Strings inside heredoc are treated as double-quoted strings, hence string interpolation is possible with heredoc.

> name = 'John'
> str = <<-HEREDOC
        My Name is
        #{name}.
  HEREDOC
=> "      My Name is\n      John.\n"
> name = 'John'
> str = <<-"HEREDOC"
        My Name is
        #{name}.
  HEREDOC
=> "      My Name is\n      John.\n"

We can opt-out the interpolation by using single-quoted heredoc start marker string. For example:

> name = 'John'
> str = <<-'HEREDOC'
        My Name is
        #{name}.
  HEREDOC
=> "      My Name is\n      #{name}.\n"

How to remove unwanted spaces?

We can remove unwanted spaces using gsub.

> str = <<-HEREDOC.gsub(/^\s+/, "")
        This is a
        sample text.
  HEREDOC
=> "This is a\nsample text.\n"  

In Rails, there is a method strip_heredoc which we can use to remove the unwanted spaces.

> str = <<-HEREDOC.strip_heredoc
        This is a
        sample text.
  HEREDOC
=> "This is a\nsample text.\n"  

Squiggly heredoc

Ruby 2.3 introduced the squiggly heredoc <<~ to remove the spaces due to indentation.

> str = <<~HEREDOC
        This is a
        sample text.
  HEREDOC
=> "This is a\nsample text.\n"  

Let’s take a look at another example:

> str = <<~HEREDOC
          Hello,\nGood Morning
          My name is John.
        HEREDOC
=> "Hello,\nGood Morning\nMy name is John.\n"
> puts str
=> Hello,
=> Good Morning
=> My name is John.

Now, we will use strip_heredoc instead of <<~.

> str = <<-HEREDOC.strip_heredoc
          Hello,\nGood Morning
          My name is John.
        HEREDOC
=> "          Hello,\nGood Morning\n          My name is John.\n"
> puts str
=>           Hello,
=> Good Morning
=>           My name is John.

As we can see that strip_heredoc didn’t remove the leading spaces in the above example because the strip_heredoc documentation says

It looks for the least indented non-empty line in the whole string and removes that amount of leading whitespace.

This is the only difference between strip_heredoc and <<~.

Convert multiline into one line

In Rails, we can use String#squish to convert multiline into one line.

> str = <<~HEREDOC.squish
        This is a
        sample text.
  HEREDOC
=> "This is a sample text."  

Shell Command

To execute shell commands, we can use backticks.

> str = <<~`HEREDOC`
        date
  HEREDOC
=> "Wed Mar 25 18:51:08 IST 2020\n" 

Summary

We looked at the different ways of defining multiline string in Ruby and Rails using heredoc.

Hopefully, it will be less confusing next time we need to make use of one of the heredoc versions.