496

Is there a way to make this look a little better?

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'

Like, is there a way to imply concatenation?

derobert
  • 49,731
  • 15
  • 94
  • 124
Zombies
  • 25,039
  • 43
  • 140
  • 225

17 Answers17

694

There are pieces to this answer that helped me get what I needed (easy multi-line concatenation WITHOUT extra whitespace), but since none of the actual answers had it, I'm compiling them here:

str = 'this is a multi-line string'\
  ' using implicit concatenation'\
  ' to prevent spare \n\'s'

=> "this is a multi-line string using implicit concatenation to eliminate spare
\\n's"

As a bonus, here's a version using funny HEREDOC syntax (via this link):

p <<END_SQL.gsub(/\s+/, " ").strip
SELECT * FROM     users
         ORDER BY users.id DESC
END_SQL
# >> "SELECT * FROM users ORDER BY users.id DESC"

The latter would mostly be for situations that required more flexibility in the processing. I personally don't like it, it puts the processing in a weird place w.r.t. the string (i.e., in front of it, but using instance methods that usually come afterward), but it's there. Note that if you are indenting the last END_SQL identifier (which is common, since this is probably inside a function or module), you will need to use the hyphenated syntax (that is, p <<-END_SQL instead of p <<END_SQL). Otherwise, the indenting whitespace causes the identifier to be interpreted as a continuation of the string.

This doesn't save much typing, but it looks nicer than using + signs, to me.

Also (I say in an edit, several years later), if you're using Ruby 2.3+, the operator <<~ is also available, which removes extra indentation from the final string. You should be able to remove the .gsub invocation, in that case (although it might depend on both the starting indentation and your final needs).

EDIT: Adding one more:

p %{
SELECT * FROM     users
         ORDER BY users.id DESC
}.gsub(/\s+/, " ").strip
# >> "SELECT * FROM users ORDER BY users.id DESC"
A. Wilson
  • 8,534
  • 1
  • 26
  • 39
  • 5
    This is an old question BUT there either is an error in the answer or has been a change in syntax since then. `p < – jaydel May 18 '16 at 14:11
  • It's only an error if the ending identifier is indented (the hyphen tells the ruby interpreter to trim whitespace before making the ending identifier determination). I can put a note mentioning that, though. Also, the ~ is unnecessary, gsub \s+ and strip are already removing leading whitespace. – A. Wilson May 18 '16 at 19:19
  • Adding `<<~` to the answer would be nice, ended up researching that from there. Personnally, I use `<<~MSG.strip ... MSG` which also strips the last `\n`. – Qortex Apr 24 '20 at 12:22
  • 1
    When I wrote this answer (nine years ago, sheesh!), Ruby was on 1.9, and <<~ (evidently) wasn't introduced until 2.3. Anyway, ancient history aside, I'll put it in, thanks for bringing it up. – A. Wilson Apr 28 '20 at 21:19
  • *Thank you* for being one of the few answers which doesn't add extra newlines, which is what I was trying to avoid when I found this question. – Josh May 14 '20 at 22:35
  • This solution causes me FrozenError: can't modify frozen String – cegprakash Jan 05 '23 at 12:48
  • `.gsub` and `.strip` mutate their targets, and frozen strings are immutable; this probably means you're using the frozen_string_literal pragma in a Rails 2.3+ project (and doing this operation to a literal string). Using one of the other answers that relies on concatenation rather than mutation should work for you (removing extra whitespaces will still require un-freezing the strings, though). – A. Wilson Jan 06 '23 at 21:15
225

In ruby 2.0 you can now just use %

For example:

    SQL = %{
      SELECT user, name
      FROM users
      WHERE users.id = #{var}
      LIMIT #{var2}
    }
Salomanuel
  • 897
  • 10
  • 22
Robbie Guilfoyle
  • 3,363
  • 1
  • 18
  • 18
  • 14
    Works in Ruby 1.9.3 too. – Andy Stewart Sep 20 '13 at 10:02
  • 49
    A string created with this syntax will include both newlines and any indention added to subsequent lines. – James May 06 '15 at 21:34
  • This is even better than < – Nasser Jun 23 '15 at 02:02
  • 2
    @Nasser A heredoc does interpolation as well. – Nic Apr 03 '16 at 01:06
  • 6
    If using Rails invoking `squish` on the output should be helpful. – Jignesh Gohel Dec 09 '16 at 14:06
  • The thing to note is that many commenters are talking about stripping/compacting commas. If you do so, then do not interpolate variables in there. Because it will obviously compact white space in your variables. Bad idea to use interpolation anyway because of injection. Use your driver placeholder system instead which escapes the variables and possibly formats them for each type. – Mig Oct 27 '20 at 10:42
180

Yes, if you don't mind the extra newlines being inserted:

 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'

Alternatively you can use a heredoc:

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
smile2day
  • 1,585
  • 1
  • 24
  • 34
Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
  • 93
    You could also use `%Q(...)` – BaroqueBobcat Feb 25 '10 at 21:38
  • 3
    @Zombies: Newlines are typically allowed in SQL statements and are just treated as ordinary whitespace. – Mark Byers Feb 25 '10 at 22:02
  • 2
    see my answer below for an example, you can just use % now. – Robbie Guilfoyle Aug 28 '13 at 01:35
  • 8
    You could also use `%(...)` – zero-divisor Oct 20 '14 at 06:49
  • 1
    Something important to keep in mind if you intentionally add trailing whitespace and use one of these solutions is that **your editor may automatically remove trailing space** when saving the file. While I normally prefer this behaviour, it has caused unexpected issues for me a few times. A solution is to write your multi-line string like how the OP did in the question. – Dennis Jan 20 '15 at 12:24
  • 1
    "if you don't mind the extra newlines being inserted" but the question asks for no newlines... – Josh May 14 '20 at 22:23
64

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

Spot the extra spaces after the terminating EOS.

Percent String Literals

See RubyDoc for how to use the percentage sign followed by a string in a parentheses pair such as a %(...), %[...], %{...}, etc. or a pair of any non-alphanumeric character such as %+...+

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.

Christopher Oezbek
  • 23,994
  • 6
  • 61
  • 85
  • 1
    This is the proper answer. Thanks for all the details! It clarified a lot of things with HEREDOC. – Rael Gugelmin Cunha Feb 21 '23 at 12:24
  • I think it will be obvious for many, but took me a while to understand that there is no difference between how `%{}`, `%()`, `%[]` strings are processed. You simply chose the terminators that are the least likely to be used in the string inside, removing (or almost eliminating) the need to do any escaping. For example, in SPARQL queries `{}` and `()` are common, and `[]` could be used for lists, it makes sense to use `%$$`. In SQL, `[]` and `()` are common, `%{}` could be a good pick (except for T-SQL, maybe). I also like to pick chars that one needs to escape in Bash, `\$` looks natural. – berezovskyi Jul 24 '23 at 13:39
50

There are multiple syntaxes for multi-line strings as you've already read. My favorite is Perl-style:

conn.exec %q{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}

The multi-line string starts with %q, followed by a {, [ or (, and then terminated by the corresponding reversed character. %q does not allow interpolation; %Q does so you can write things like this:

conn.exec %Q{select attr1, attr2, attr3, attr4, attr5, attr6, attr7
      from #{table_names},
      where etc etc etc etc etc etc etc etc etc etc etc etc etc}

I actually have no idea how these kinds of multi-line strings are called so let's just call them Perl multilines.

Note however that whether you use Perl multilines or heredocs as Mark and Peter have suggested, you'll end up with potentially unnecessary whitespaces. Both in my examples and their examples, the "from" and "where" lines contain leading whitespaces because of their indentation in the code. If this whitespace is not desired then you must use concatenated strings as you are doing now.

Hongli
  • 18,682
  • 15
  • 79
  • 107
  • 4
    from #{table_names} would not work in this example, as you used %q{}, it would work if you used %q[] or () – MatthewFord Jun 16 '11 at 17:22
  • 2
    My favorite in this vein is just %{ super multiline string with interpolation support } – Duke Oct 21 '11 at 00:58
  • strings produced from the `%q` family will include the newlines which is not equivilent to the original code. – Josh May 14 '20 at 22:24
31

Sometimes is worth to remove new line characters \n like:

conn.exec <<-eos.squish
 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
Kamil Lelonek
  • 14,592
  • 14
  • 66
  • 90
25

You can also use double quotes

x = """
this is 
a multiline
string
"""

2.3.3 :012 > x
 => "\nthis is\na multiline\nstring\n"

If needed to remove line breaks "\n" use backslash "\" at the end of each line

juliangonzalez
  • 4,231
  • 2
  • 37
  • 47
  • 6
    You can achieve the same result with the singular double quotes. There is no such thing like triple double quotes in Ruby. It just interprets them as `"" + "double quotes with some content" + ""`. – rakvium May 07 '18 at 14:04
  • Yeah, but ` ""+"\n hello\n "+"" Does look weird – juliangonzalez Nov 06 '18 at 22:36
  • 1
    Yes, it looks weird, and this is why there is no reason to add extra double quotes when you can just use the singular double quotes with the same result. – rakvium Nov 12 '18 at 08:20
  • Yes, I meant the plus sign. The double quotes without it look just fine, its readable and and easier to spot instead of a single quote, which should be used on single line strings. – juliangonzalez Nov 12 '18 at 11:26
  • 1
    I mean that just `"x"` looks better and works faster than `"""x"""` (which is basically the same as `""+"x"+""`) or `"""""x"""""` (which is the same as `"" + "" + "x" + "" + ""`). It's Ruby, not Python, where you use `"""` instead of `"` when you need a multi-line string. – rakvium Nov 27 '18 at 17:46
  • double quotes will include the newlines which is not equivalent to the original code. – Josh May 14 '20 at 22:24
  • @Josh that's why I put a not at the end to use `\\` if no new lines are needed. You didn't need to downvote. – juliangonzalez May 15 '20 at 20:39
  • Apologies, I missed that while scouring this page for an answer which did what I needed. – Josh May 15 '20 at 21:11
17

Other options:

#multi line string
multiline_string = <<EOM
This is a very long string
that contains interpolation
like #{4 + 5} \n\n
EOM

puts multiline_string

#another option for multiline string
message = <<-EOF
asdfasdfsador #{2+2} this month.
asdfadsfasdfadsfad.
EOF

puts message
Alex Cohen
  • 5,596
  • 16
  • 54
  • 104
16

Recently with the new features in Ruby 2.3 the new squiggly HEREDOC will let you write our multiline strings in a nice manner with a minimal change so using this combined with the .squish (if you are using rails) will let you write multiline in a nice way! in case of just using ruby, you can do a <<~SQL.split.join(" ") which is almost the same

[1] pry(main)> <<~SQL.squish
[1] pry(main)*   select attr1, attr2, attr3, attr4, attr5, attr6, attr7
[1] pry(main)*   from table1, table2, table3, etc, etc, etc, etc, etc,
[1] pry(main)*   where etc etc etc etc etc etc etc etc etc etc etc etc etc
[1] pry(main)* SQL
=> "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"

ref: https://infinum.co/the-capsized-eight/multiline-strings-ruby-2-3-0-the-squiggly-heredoc

Mshka
  • 1,798
  • 1
  • 10
  • 19
15
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
Peter
  • 127,331
  • 53
  • 180
  • 211
  • 3
    using heredoc without the '-', as in '<<-eos', will include the additional leader spaces. see Mark Byers' response. – ives Feb 02 '16 at 02:51
  • 1
    heredoc will include the newlines which is not equivalent to the original code. – Josh May 14 '20 at 22:24
9

To avoid closing the parentheses for each line you can simply use double quotes with a backslash to escape the newline:

"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"
Pwnrar
  • 1,307
  • 13
  • 13
6

If you do mind extra spaces and newlines, you can use

conn.exec %w{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} * ' '

(use %W for interpolated strings)

UncleGene
  • 2,132
  • 16
  • 19
6
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'

<< is the concatenation operator for strings

  • 3
    `+` is the regular concatenation operator, `<<` is the *in-place* append operator. Using side effects on a literal happens to work here (the first string is modified twice and returned) but IMHO it's weird and makes me do a double-take, where `+` would be perfectly clear. But maybe I'm just new to Ruby... – Beni Cherniavsky-Paskin Apr 18 '16 at 11:20
  • 1
    This won't work if `frozen_string_literal` is enabled – Raido Jul 06 '17 at 12:56
5
conn.exec [
  "select attr1, attr2, attr3, ...",
  "from table1, table2, table3, ...",
  "where ..."
].join(' ')

This suggestion has the advantage over here-documents and long strings that auto-indenters can indent each part of the string appropriately. But it comes at an efficiency cost.

Aidan Cully
  • 5,457
  • 24
  • 27
  • 1
    @Aidan, You can replace the commas with backslashes (a la C) and no join (or array) will be needed: The interpreter will concatenate the strings at (I think) parse time, making it pretty quick compared to most of the alternatives. One advantage, though, of joining an array of strings is that some auto-indenters do nicer work than they do with, for example, here-doc strings or with \. – Wayne Conrad Feb 26 '10 at 15:18
  • 1
    One note, the heredoc syntax <<- will allow appropriate indentation. – A. Wilson May 18 '11 at 12:41
5

Elegant Answer Today:

<<~TEXT
Hi #{user.name}, 

Thanks for raising the flag, we're always happy to help you.
Your issue will be resolved within 2 hours.
Please be patient!

Thanks again,
Team #{user.organization.name}
TEXT

Theres a difference in <<-TEXT and <<~TEXT, former retains the spacing inside block and latter doesn't.

There are other options as well. Like concatenation etc. but this one makes more sense in general.

If I am wrong here, let me know how...

Sandip Mane
  • 1,420
  • 1
  • 9
  • 14
4

Like you, I was also looking for a solution which does not include newlines. (While they may be safe in SQL, they're not safe in my case and I have a large block of text to deal with)

This is arguably just as ugly, but you can backslash-escape newlines in a heredoc to omit them from the resulting string:

conn.exec <<~END_OF_INPUT
    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
  END_OF_INPUT

Note that you cannot due this without interpolation (I.E. <<~'END_OF_INPUT') so be careful. #{expressions} will be evaluated here, whereas they will not in your original code. A. Wilson's answer may be better for that reason.

Josh
  • 10,961
  • 11
  • 65
  • 108
0

do you want mutli line in ruby use triple quotes, you can run this in python either.

# to print multi line string

print("""
hello i am mario and this
is multi line it is same in python
""")