This question made me go off a rabbit hole to understand how HEREDOC works. Excuse me if the answer became too long.
The squiggly HEREDOC <<~
is what you are looking for when you want to define a multi-line string with newlines and proper indentation (available since Ruby 2.3):
conn.exec <<~EOS
select attr1, attr2, attr3, attr4, attr5, attr6, attr7
from table1, table2, table3, etc, etc, etc, etc, etc
where etc etc etc etc etc etc etc etc etc etc etc etc etc
EOS
# -> "select...\nfrom...\nwhere..."
If proper indentation is not a concern, then single and double quotes can span multiple lines in Ruby:
conn.exec "select attr1, attr2, attr3, attr4, attr5, attr6, attr7
from table1, table2, table3, etc, etc, etc, etc, etc,
where etc etc etc etc etc etc etc etc etc etc etc etc etc"
# -> "select...\n from...\n where..."
If single or double quotes are cumbersome because that would need lots of escaping, then the percent string literal notation %
is the most flexible solution:
conn.exec %(select attr1, attr2, attr3, attr4, attr5, attr6, attr7
from table1, table2, table3, etc, etc, etc, etc, etc
where (ProductLine = 'R' OR ProductLine = "S") AND Country = "...")
# -> "select...\n from...\n where..."
If the aim is to avoid the newlines (which both the squiggly HEREDOC, quotes and the percent string literal will cause), then a line continuation can be used by putting a backslash \
as the last non-whitespace character in a line. This will continue the line and will cause Ruby to concatenate the Strings back to back (watch out for those spaces inside the quoted string):
conn.exec 'select attr1, attr2, attr3, attr4, attr5, attr6, attr7 ' \
'from table1, table2, table3, etc, etc, etc, etc, etc, ' \
'where etc etc etc etc etc etc etc etc etc etc etc etc etc'
# -> "select...from...where..."
If you use Rails, then String.squish
will strip the string of leading and trailing space and collapse all consecutive whitespaces (newlines, tabs, and all) into a single space:
conn.exec "select attr1, attr2, attr3, attr4, attr5, attr6, attr7
from table1, table2, table3, etc, etc, etc, etc, etc,
where etc etc etc etc etc etc etc etc etc etc etc etc etc".squish
# -> "select...attr7 from...etc, where..."
More details:
Ruby HEREDOC Syntax
The Here Document Notation for Strings is a way to designate long blocks of text inline in code. It is started by <<
followed by a user-defined String (the End of String terminator). All following lines are concatenated until the End of String terminator is found at the very beginning of a line:
puts <<HEREDOC
Text Text Text Text
Bla Bla
HEREDOC
# -> "Text Text Text Text\nBlaBla"
The End of String terminator can be chosen freely, but it is common to use something like "EOS" (End of String) or something that matches the domain of the String such as "SQL".
HEREDOC supports interpolation by default or when the EOS terminator is double quoted:
price = 10
print <<"EOS" # comments can be put here
1.) The price is #{price}.
EOS
# -> "1.) The price is 10."
Interpolation can be disabled if the EOS terminator is single quoted:
print <<'EOS' # Disabled interpolation
3.) The price is #{price}.
EOS
# -> "3.) The price is #{price}."
One important restriction of the <<HEREDOC
is that the End of String terminator needs to be at the beginning of the line:
puts <<EOS
def foo
print "foo"
end
EOS
EOS
#-> "....def foo\n......print "foo"\n....end\n..EOS"
To get around this, the <<-
syntax was created. It allows the EOS terminator to be indented to make the code look nicer. The lines between the <<-
and EOS terminator are still used in their full extend including all indentation:
def printExample
puts <<-EOS # Use <<- to indent End of String terminator
def foo
print "foo"
end
EOS
end
# -> "....def foo\n......print "foo"\n....end"
Since Ruby 2.3, we now have the squiggly HEREDOC <<~
which removes leading whitespace:
puts <<~EOS # Use the squiggly HEREDOC <<~ to remove leading whitespace (since Ruby 2.3!)
def foo
print "foo"
end
EOS
# -> "def foo\n..print "foo"\nend"
Empty lines and lines which only contains tabs and space are ignored by <<~
puts <<~EOS.inspect
Hello
World!
EOS
#-> "Hello\n..World!"
If both tabs and spaces are used, tabs are considered as equal to 8 spaces.
If the least-indented line is in the middle of a tab, this tab is not removed.
puts <<~EOS.inspect
<tab>One Tab
<space><space>Two Spaces
EOS
# -> "\tOne Tab\nTwoSpaces"
HEREDOC can do some crazy stuff such as executing commands using backticks:
puts <<`EOC`
echo #{price}
echo #{price * 2}
EOC
HEREDOC String definitions can be "stacked", which means that the first EOS terminator (EOSFOO below) will end the first string and start the second (EOSBAR below):
print <<EOSFOO, <<EOSBAR # you can stack them
I said foo.
EOSFOO
I said bar.
EOSBAR
I don't think anybody would ever use it as such, but the <<EOS
is really just a string literal and can be put whereever a string can normally be put:
def func(a,b,c)
puts a
puts b
puts c
end
func(<<THIS, 23, <<THAT)
Here's a line
or two.
THIS
and here's another.
THAT
If you don't have Ruby 2.3, but Rails >=
3.0 then you can use String.strip_heredoc
which does the same as <<~
# File activesupport/lib/active_support/core_ext/string/strip.rb, line 22
class String
def strip_heredoc
gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, "".freeze)
end
end
puts <<-USAGE.strip_heredoc # If no Ruby 2.3, but Rails >= 3.0
This command does such and such.
Supported options are:
-h This message
...
USAGE
Troubleshooting
If you see errors when Ruby parses your file, then it is most likely that you either have extra leading or trailing spaces with a HEREDOC or extra trailing spaces with a squiggly HEREDOC. For example:
What you see:
database_yml = <<~EOS
production:
database: #{fetch(:user)}
adapter: postgresql
pool: 5
timeout: 5000
EOS
What Ruby tells you:
SyntaxError: .../sample.rb:xx: can't find string "EOS" anywhere before EOF
...sample.rb:xx: syntax error, unexpected end-of-input, expecting `end'
What is at fault:
Spot the extra spaces after the terminating EOS.
Percent String Literals
See RubyDoc for how to use the percentage string literals. Percentage string literals start with a percentage sign %
followed either an opening parenthesis or any non-alphanumeric character. Then you put your string and end the string literal with the associated closing parenthesis symbol or the non-alphanumeric character.
Thus you could use %(...)
, %[...]
, %{...}
or e.g. %+...+
.
No matter which symbol or parentheses (round, square, curly) the result is always the same: Just a string literal.
You simply chose the terminators that are the least likely to be used in the string inside, reducing the need to do escaping. For example, in SPARQL queries {}
and ()
and []
are all used, so it might make sense to use %$...$
. In SQL, []
and ()
are common, thus %{}
could be a good pick.
Last Words
Last, to get the answer to the original question "Is there a way to imply concatenation?"
answered: Ruby always implies concatenation if two strings (single and double quoted) are found back to back:
puts "select..." 'from table...' "where..."
# -> "select...from table...where..."
The caveat is that this does not work across line-breaks, because Ruby is interpreting an end of statement and the consequitive line of just strings alone on a line doesn't do anything.
p <<END_SQL
should bep <<-END_SQL
Otherwise this is The Answer. optionally you can strip leading whitespace with the squiggly HEREDOC operator,<<~END_SQL
– Vatic