292

We recently decided at my job to a ruby style guide. One of the edicts is that no line should be wider than 80 characters. Since this is a Rails project, we often have strings that are a little bit longer - i.e. "User X wanted to send you a message about Thing Y" that doesn't always fit within the 80 character style limit.

I understand there are three ways to have a long string span multiple lines:

  • HEREDOC
  • %Q{}
  • Actual string concatenation.

However, all of these cases end up taking more computation cycles, which seems silly. String concatenation obviously, but for HEREDOC and %Q I have to strip out the newlines, via something like .gsub(/\n$/, '').

Is there a pure syntax way to do this, that is equivalent to just having the whole string on one line? The goal being, obviously, to not spend any extra cycles just because I want my code to be slightly more readable. (Yes, I realize that you have to make that tradeoff a lot...but for string length, this just seems silly.)

Update: Backslashes aren't exactly what I want because you lose indentation, which really affects style/readability.

Example:

if foo
  string = "this is a \  
string that spans lines"  
end

I find the above a bit hard to read.

EDIT: I added an answer below; three years later we now have the squiggly heredoc.

chug2k
  • 5,106
  • 2
  • 25
  • 30
  • It may be worth your while to factor out those strings. – Cheezmeister Dec 31 '13 at 00:49
  • 4
    possible duplicate of [Ruby: Can I write multi-line string with no concatenation?](http://stackoverflow.com/questions/2337510/ruby-can-i-write-multi-line-string-with-no-concatenation) – givanse Feb 12 '14 at 19:09
  • 2
    It's unclear whether you want to keep `\n` newline characters or not. The top answer doesn't keep them—yet your answer does. The question says "without stripping newlines"—yet the description says "[...] I have to strip out the newlines." – Bryan Dimas Feb 21 '17 at 16:38

8 Answers8

540

Maybe this is what you're looking for?

string = "line #1"\
         "line #2"\
         "line #3"

p string # => "line #1line #2line #3"
Jessehz
  • 5,178
  • 2
  • 15
  • 13
  • 4
    huh. yeah, that's pretty perfect. is that actually doing a concatenate under the hood? (hiding a +?) – chug2k May 09 '12 at 21:26
  • 13
    I'm fairly sure that it's not; I tried both undef'ing and redefining String#+ and it doesn't seem like anything is being sent there. – Jessehz May 09 '12 at 22:08
  • Also, putting the strings next to each other on the same line also works, but that isn't very useful. – Jessehz May 09 '12 at 22:11
  • 3
    I also like Emily's solution. But for some reason I'm more comfortable with this one because leading white-spaces on the following lines can be indented without second-guessing oneself. – Amin Ariana Mar 19 '13 at 16:40
  • 6
    Re concatenation, check out Amadan's answer to [this related question](http://stackoverflow.com/questions/27792508/ruby-backslash-to-continue-string-on-a-new-line). He uses the bytecode to prove it is not concatenating strings but treating them as one long one. – Tom Hundt Mar 17 '17 at 19:57
  • In Ruby, adjacent strings in a statement are concatenated during parsing (as opposed to concatenating during runtime using the String#+ operator method). The line continuation ensures the strings appear to the interpreter as being part of the same statement. So `str = "str #1" "str #2" "str #3"` is the same as `str = "str #1str #2str #3"`. – Jordan Pickwell Feb 13 '20 at 17:28
70

You can use \ to indicate that any line of Ruby continues on the next line. This works with strings too:

string = "this is a \
string that spans lines"

puts string.inspect

will output "this is a string that spans lines"

Emily
  • 17,813
  • 3
  • 43
  • 47
  • 11
    huh. that is pretty simple. well, it's probably too much to ask, but I can't indent the next line. it'd be ideal if I could match it up with the equals sign. – chug2k May 09 '12 at 20:35
  • 6
    Note: you can't have any white space after that backslash. It must be the last character. – Julian Mann Jun 09 '16 at 07:13
  • 3
    See the answer by @Jessehz. The way to have nicely formatted code with proper indenting is to put each line in quotes. – orrd Jun 05 '17 at 20:41
62

Three years later, there is now a solution in Ruby 2.3: The squiggly heredoc.

class Subscription
  def warning_message
    <<~HEREDOC
      Subscription expiring soon!
      Your free trial will expire in #{days_until_expiration} days.
      Please update your billing information.
    HEREDOC
  end
end

Blog post link: https://infinum.co/the-capsized-eight/articles/multiline-strings-ruby-2-3-0-the-squiggly-heredoc

The indentation of the least-indented line will be removed from each line of the content.

chug2k
  • 5,106
  • 2
  • 25
  • 30
  • 38
    For anyone else wondering: `<<~HEREDOC` keeps the newline characters, it only rids of the indentation. E.g. `line 1\nline 2\nline 3\n` – Bryan Dimas Feb 21 '17 at 16:37
  • 2
    Stripping newlines will still require extra computation, though, which the OP sort of implied was not desirable. – henrebotha Mar 06 '17 at 13:42
  • 1
    @henrebotha Yes, but OP said more explicitly they want the "equivalent to just having the whole string on one line", which would require no newlines. – Nick Apr 24 '19 at 15:15
  • 3
    So you explicitly stated in your question that you did not want to use a heredoc and have to strip out the newlines. Your answer here uses a heredoc and requires that "post-processing" you wanted to avoid. – lamont Jul 08 '19 at 21:54
24

This is by now a very old question but as the issue still seems to come up here's an updated answer. Since the original poster indicated this was for a Rails project you can look to Rails' String inflections for help.

my_long_string = <<-STRING
 hello
    there
 multiline
      multiindented string
STRING

=> " hello\n    there\n multiline\n      multiindented string\n"

Enter the squish method.

my_long_string = <<-STRING.squish
 so
    long
 multiline
      multiindented string
STRING

=> "so long multiline multiindented string"

As per the docs -

squish() Returns the string, first removing all whitespace on both ends of the string, and then changing remaining consecutive whitespace groups into one space each.

geoff
  • 416
  • 3
  • 5
22

I had this problem when I try to write a very long url, the following works.

image_url = %w(
    http://minio.127.0.0.1.xip.io:9000/
    bucket29/docs/b7cfab0e-0119-452c-b262-1b78e3fccf38/
    28ed3774-b234-4de2-9a11-7d657707f79c?
    X-Amz-Algorithm=AWS4-HMAC-SHA256&
    X-Amz-Credential=ABABABABABABABABA
    %2Fus-east-1%2Fs3%2Faws4_request&
    X-Amz-Date=20170702T000940Z&
    X-Amz-Expires=3600&X-Amz-SignedHeaders=host&
    X-Amz-Signature=ABABABABABABABABABABAB
    ABABABABABABABABABABABABABABABABABABA
).join

Note, there must not be any newlines, white spaces when the url string is formed. If you want newlines, then use HEREDOC.

Here you have indentation for readability, ease of modification, without the fiddly quotes and backslashes on every line. The cost of joining the strings should be negligible.

Zack Xu
  • 11,505
  • 9
  • 70
  • 78
19

I modified Zack's answer since I wanted spaces and interpolation but not newlines and used:

%W[
  It's a nice day "#{name}"
  for a walk!
].join(' ')

where name = 'fred' this produces It's a nice day "fred" for a walk!

iheggie
  • 2,011
  • 23
  • 23
2

Squish in Rails is doing what you need.

https://apidock.com/rails/String/squish

End it looks like this:

%{ Multi-line
   string }.squish

And because you need it in Ruby, just look at Rails code:

%{ Multi-line
   string }
  .gsub!(/[[:space:]]+/, " ")
  .strip!
viktorianer
  • 92
  • 2
  • 5
0

You can concatenate multiple strings split over several lines:

if foo
  string = "this is a" + 
           "string that spans lines"  
end
Matias Korhonen
  • 886
  • 2
  • 8
  • 21
Addin Cui
  • 63
  • 6