Heredoc in Ruby


2014-06-12 · 3 min read

Ruby supports multiline strings through « here doc » syntax. It allows to specify a string as a block of text where newlines and indents are preserved. It is not widely known, but useful feature.

There are two types of « here doc » syntax in Ruby:

def display
  text = <<TEXT
  Lorem ipsum dolor sit amet,
     consectetur adipisicing elit,
TEXT # without dash
end

def display
  text = <<-TEXT
  Lorem ipsum dolor sit amet,
     consectetur adipisicing elit,
  TEXT # with dash
end
  • using << the block end marker is aligned with the most left column of textual area
  • using <<- it is aligned with the indent level introduced by the « heredoc » block.

In both case the text inside is indented relatively to the first column. This is not what you may expect, especially when using the <<- syntax. In order to preserve the formatting and remove blank characters up to the column where heredoc starts, we will write a simple unindent method and then use it on the opening marker.

class String
  def unindent
    gsub(/^#{scan(/^\s*/).min_by{|l|l.length}}/, "")
  end
end

def display
  text = <<-TEXT.unindent
  Lorem ipsum dolor sit amet,
     consectetur adipisicing elit,
  TEXT # with dash
end

Note that in Rails there is a similiar function, implemented in active_support/core_ext/string/strip.rb:

class String
  def strip_heredoc
    indent = scan(/^[ \t]*(?=\S)/).min.try(:size) || 0
    gsub(/^[ \t]{#{indent}}/, '')
  end
end

« here doc » can be passed as a parameter

if True
  h1 = { name: "Foo", desc1: <<-desc1.unindent desc2: <<-desc2.unindent }
  Lorem ipsum dolor sit amet,
    consectetur adipisicing elit,

  <<-desc1
    Mauris vehicula tortor eros, sed imperdiet velit aliquet non.
      Nullam
         eget elit nibh.
  <<-desc2

  p1 = Person.new(name: <<-name.unindent)
   this is test
     and this is test again
  name

  p2 = Person.new(name: <<-name.unindent
   this is test
     and this is test again
  name
  )

  p3 = Person.new(name: "
   this is test
     and this is test again".unindent)

Variables are interpolated in « here docs » as within strings.

def display
  name = "Zaiste"
  text = <<-TEXT
  Lorem ipsum dolor #{name} amet,
     consectetur adipisicing elit,
  TEXT
end

In order to avoid interpolation in « here docs », enclose the opening marker with single quotes. It will make the content behave like a single-quoted string.

def display
  name = "Zaiste"
  text = <<-'TEXT'
  Lorem ipsum dolor #{name} amet,
     consectetur adipisicing elit,
  TEXT
end

You can also use backticks to execute a shell command:

puts <<-`command`
  date
command

Check this gist as a reference for this article.