How to Access a Variable Within a Heredoc in Ruby

Can I access a variable within a heredoc in Ruby?

You can interpolate just like in normal strings

<<-TERMINATOR
Example #{p[:name]} blah blah blah
TERMINATOR

How can I show the results of a block within a here document (heredoc)?

String interpolation just evaluates the expression in #{ } and calls to_s on the return value and concatenates the prefix, the string returned and the postfix together to form a new string.

You code calls each, which will return the original hash, so you see the to_s return value of the hash is in the output. And the block attached to each is evaluated before the HEREDOC parameter is passed to puts <<COLORS, so the output of the puts "#{...}" in the block appears before the desired output.

You need to return the required part of the string. So, you have to do some transform of the original hash and then join the parts together with "\n" to form the "colors" part of the output you need.

puts <<COLORS
--------------------------------------------------------------------------------
The colors of the rainbow:

#{ colors.map { |key, value| "#{key} (#{value})" }.join("\n") }
--------------------------------------------------------------------------------
COLORS

I would store the transform and join result in a variable before puts and refer to that variable using string interpolation. I think it would be better to keep it simple in #{ }.

Add if in Ruby heredocs

Approach 1: do the same thing you'd do with "normal" strings.

"#{product.images.present? ? product.images.first.image_url : ''}"

Approach 1+: extract the expression above into a variable, so that your template contains less logic.

Approach 2: don't abuse heredocs and use ERB instead.

Using a heredoc as a hash value

Add a comma after your <<-RUBY:

Embed.toggler({
title: <<-RUBY,
#{entry['time']}
#{entry['group']['who']
#{entry['name']}
RUBY
content: content
})

this does work in general. I am not sure why it wasn't working in my code though.

It didn't work because hashes require key/value pair to be separated by a comma, like {title: 'my title', content: 'my content' } and your code just didn't have the comma. It was hard to see that because of the cumbersome HEREDOC syntax.

Do you know if there is a way to perform operations on the string?

You're playing with fire. It's always safer (and usually cleaner) to extract a variable and do post-processing on a variable itself:

title = <<-RUBY
#{entry['time']}
#{entry['group']['who']
#{entry['name']}
RUBY

Embed.toggler(title: title.upcase, content: content)

However, if you feel dangerous today, you can just add operations after opening HEREDOC literal, just as you've added the comma:

Embed.toggler({
title: <<-RUBY.upcase,
#{entry['time']}
#{entry['group']['who']
#{entry['name']}
RUBY
content: content
})

But I discourage you from this because it destroys readability.

Can you put a conditional statement inside a here-doc?

Yes, you can. (Did you try it?) HEREDOCs declared as you did act like a double-quoted string. If you happened to want the reverse, you would single-quote your HEREDOC indicator like so:

str = <<EOF
#{ "this is interpolated Ruby code" }
EOF

str = <<'EOF'
#{ This is literal text }
EOF

The "green" and "blue" in your example are wrong, unless you have methods or local variables with those names. You probably wanted either:

str = <<EOF
The sky is #{if sky==1 then 'blue' else 'green' end}
EOF

...or the terser version:

str = <<EOF
The sky is #{sky==1 ? :blue : :green}
end

As with all string interpolation, the result of each expression has #to_s called on it. As the string representation of a symbol is the same text, using symbols in interpolation like that saves one character when typing. I use it most often like:

cats = 13
str = "I have #{cats} cat#{:s if cats!=1}"

What are -- Ruby Strings called? And how do I insert variables in them?

That syntax is for declaring a HERE DOCUMENT
http://www.ruby-doc.org/docs/ruby-doc-bundle/Manual/man-1.4/syntax.html#here_doc

There's a line-oriented form of the string literals that is usually
called as `here document'. Following a << you can specify a string or
an identifier to terminate the string literal, and all lines following
the current line up to the terminator are the value of the string. If
the terminator is quoted, the type of quotes determines the type of
the line-oriented string literal. Notice there must be no space
between << and the terminator.

If the - placed before the delimiter, then all leading whitespcae
characters (tabs or spaces) are stripped from input lines and the line
containing delimiter. This allows here-documents within scripts to be
indented in a natural fashion.

Regarding interpolation, the link gives more details, but it is like a double quoted string if your string is delimited as below (ignore this page's color formatting)

<<-HERE
I can interpolate #{foo}
HERE

whereas it is like a single quoted string

<<-'HERE'
This will print out #{foo} as text
HERE

Also the original pickaxe is a good source http://www.ruby-doc.org/docs/ProgrammingRuby/html/language.html

Ruby HERE-DOC method parameter passing

My guess is that this is what you are trying to do:

def meth2(str1, str2)
str1.upcase + "..." + str2.downcase
end

str2 = meth2(<<EOF1, <<EOF2)
some string
EOF1
ANOTHER STRING
EOF2

str2 # => " SOME STRING\n... another string\n"

If you don't intent to indent, see here. ← See my play with words here?

Bracket operators inside here-document as argument?

The problem is that heredocs have a somewhat confusing structure that breaks the normal flow of code. The content of a heredoc begins on the line immediately after the <<EOF that opens the heredoc and ends at the EOF that closes the heredoc but the expression that contains the heredoc continues on from left to right as normal.

The structure of:

method(<<EOF)[0][0]
lots of text
EOF

is actually more like this:

      /<<EOF       \
|lots of text|
|lots of text|
method|lots of text|[0][0]
|lots of text|
|lots of text|
\EOF /

where the slashes and vertical bars are a crude attempt to draw very tall ASCII art parentheses; or, if you have a proper unicode font:

      ⎛<<EOF       ⎞
⎜lots of text⎟
⎜lots of text⎟
method⎜lots of text⎟[0][0]
⎜lots of text⎟
⎜lots of text⎟
⎝EOF ⎠

You can think of heredocs as a funny looking double quote (or %Q(...) if you prefer) that goes vertically rather than horizontally like the rest of your code.

It would (IMO) be more consistent to write:

method(<<EOF
lots of text
EOF)[0][0]

but heredocs have a long history (going all the way back to /bin/sh) that we're stuck with.

Back to the real question: the [0][0] part of that expression isn't inside the heredoc at all, that's simply applied to what method("lots of text\nlots of text\n...") returns.



Related Topics



Leave a reply



Submit