Question

I'd like to invoke a block from within a here document (heredoc), but I can't seem to get it to work. Am I misunderstanding the syntax, or is this not really possible (using Ruby 1.9.3)?

The following sample code illustrates what I am trying to do.

colors = { "red"    => "#FF0000",
           "orange" => "#FF7F00",
           "yellow" => "#FFFF00",
           "green"  => "#00FF00",
           "blue"   => "#0000FF",
           "indigo" => "#4B0082",
           "violet" => "#8F00FF" }

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

#{ colors.each { |key, value| puts "#{key} (#{value})" } }
--------------------------------------------------------------------------------
COLORS

This produces the following output on my system.

red (#FF0000)
orange (#FF7F00)
yellow (#FFFF00)
green (#00FF00)
blue (#0000FF)
indigo (#4B0082)
violet (#8F00FF)
--------------------------------------------------------------------------------
The colors of the rainbow:

{"red"=>"#FF0000", "orange"=>"#FF7F00", "yellow"=>"#FFFF00", "green"=>"#00FF00", "blue"=>"#0000FF", "indigo"=>"#4B0082", "violet"=>"#8F00FF"}
--------------------------------------------------------------------------------

However, I was expecting the following.

--------------------------------------------------------------------------------
The colors of the rainbow:

red (#FF0000)
orange (#FF7F00)
yellow (#FFFF00)
green (#00FF00)
blue (#0000FF)
indigo (#4B0082)
violet (#8F00FF)
--------------------------------------------------------------------------------
Was it helpful?

Solution

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 #{ }.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top