Question
In a Ruby on Rails project that follows an 80-character line limit, how can a long string be written across multiple source-code lines without inserting newline characters into the resulting string?
For example, this message may be too long for one line:
"User X wanted to send you a message about Thing Y"
Heredocs, %Q{}, and explicit concatenation can span lines, but multiline forms may include newline characters that then need to be removed. Is there Ruby syntax that produces exactly the same value as a single-line string literal, without requiring newline stripping or unnecessary runtime work?
Backslash line continuation is also less desirable when it makes indentation difficult to read, such as:
if foo
string = "this is a \
string that spans lines"
end
Short Answer
Ruby can join adjacent string literals automatically. Put separate quoted literals next to one another, including on indented lines, and Ruby creates one string without inserting spaces or newline characters. This is a clean way to wrap static text while keeping source lines short.
Concept
A newline in your source code is not necessarily a newline in a Ruby string value.
Ruby allows two or more string literals placed next to each other to be treated as one combined literal:
message = "User X wanted to send you "
"a message about Thing Y"
The value is:
"User X wanted to send you a message about Thing Y"
There is no \n between you and a. The indentation before the second literal is only formatting for people reading the code.
This differs from a multiline quoted string or a heredoc, where pressing Enter is usually part of the string content. Adjacent literals are especially useful for static UI text, error messages, URLs, and SQL fragments that need to obey a line-length rule.
For literal-only strings, Ruby handles this as part of parsing the source rather than as a repeated runtime String#+ operation. In ordinary application code, the main reason to choose this style is clarity; performance should only be measured when it is genuinely important.
Mental Model
Think of adjacent string literals as pieces of a sentence printed on separate cards. Ruby reads the cards in order and places them edge to edge:
"Hello, " "Ruby!"
The cards are separate in the source file so they are easy to arrange, but their text becomes one message:
Hello, Ruby!
A heredoc is more like writing directly on a multi-line note: the line breaks on the note are normally preserved. Adjacent literals are separate cards, so the layout of the cards does not become part of the message.
Syntax and Examples
Use quoted string literals directly beside each other. You may separate them with spaces, a newline, and indentation.
message = "User X wanted to send you "
"a message about Thing Y"
puts message
# User X wanted to send you a message about Thing Y
The space after you is intentional. Ruby does not add a space automatically.
label = "Account settings"
" are unavailable"
puts label
# Account settings are unavailable
You can use this inside an expression as well:
if foo
string = "This is a "
"readable string that spans source lines."
end
When interpolation is needed, keep it in the relevant piece:
user_name = "Ava"
message = "Hello, #{user_name}. "
"Your report is ready."
# "Hello, Ava. Your report is ready."
Use double quotes when a piece needs interpolation or escape sequences. Single quotes are suitable for plain literal text:
path = 'app/'
'services/'
'report_builder.rb'
# "app/services/report_builder.rb"
Step by Step Execution
Consider this code:
name = "Mina"
message = "Hello, #{name}. "
"You have a new notification."
- Ruby assigns
"Mina"toname. - Ruby reads the first part of
message:"Hello, #{name}. ". #{name}is interpolated, producing"Hello, Mina. ".- Ruby reads the immediately adjacent second literal:
"You have a new notification.". - Ruby joins the literal parts with no automatic separator.
messagehas this value:
"Hello, Mina. You have a new notification."
The newline after the first closing quote and the leading spaces before the next quote are source formatting only. They are not included in message.
Real World Use Cases
-
Flash messages: Keep long Rails notices readable.
flash[:notice] = "Your password has been changed. " "Sign in again on your other devices." -
Validation errors: Write descriptive errors without a very wide source line.
error = "The selected plan cannot be used because " "your organization has reached its member limit." -
API documentation or request descriptions: Build static headers and descriptions that remain easy to review.
-
URLs and file paths: Split fixed segments where it improves readability.
docs_url = "https://docs.example.com/" "guides/account-security" -
SQL fragments: Format a fixed query carefully, while still using parameter binding for dynamic values.
query = "SELECT id, email FROM users " "WHERE active = true ORDER BY email"
Real Codebase Usage
In production Ruby code, adjacent literals are most useful when the text is fixed and wrapping at a meaningful boundary makes it easier to scan.
Keep word boundaries explicit
Put a space in one of the pieces when words must be separated:
message = "Your export is complete. "
"Download it from the reports page."
Combine with guard clauses
Long error messages often appear in validation or early-return paths:
def deliver_report(user)
return false unless user.verified?
notifier.send(
"Your requested report is ready. "
"It will remain available for seven days."
)
end
Prefer parameters for changing data
Do not assemble SQL by interpolating untrusted values. Adjacent literals improve formatting, but they do not provide security:
User.where(
"status = ? AND created_at >= ? "
"AND created_at < ?",
"active",
start_time,
end_time
)
Use a heredoc when line breaks are meaningful
For an email body, Markdown, or multi-line SQL intended to contain line breaks, a heredoc expresses that intent more clearly than adjacent literals.
Common Mistakes
Forgetting the space between words
Ruby does not insert one for you.
# Incorrect result: "Hello,world!"
greeting = "Hello,"
"world!"
Add a space inside either literal:
greeting = "Hello, "
"world!"
Expecting indentation to appear in the value
text = "first "
"second"
# "first second", not "first second"
Leading spaces outside the quotes are source indentation, not string content.
Using a multiline quoted string accidentally
This includes a newline in the result:
text = "first line
second line"
# "first line\nsecond line"
Use adjacent literals if the output must not contain a newline:
text = "first line "
"second line"
Using + when literals are enough
This works, but is noisier for fixed text:
text = "first part " +
"second part"
For literal pieces, omit +:
text = "first part "
"second part"
Use + only when joining values that are computed at runtime.
Removing newlines after creating them
Calling gsub, chomp, or strip to compensate for a formatting choice can obscure the desired value. Prefer a representation that creates the correct value directly. If text is intentionally multiline, preserve it rather than stripping it accidentally.
Comparisons
| Technique | Does it add a newline? | Best use |
|---|---|---|
Adjacent literals: "a" "b" | No | Wrapping fixed text with no line break |
Explicit concatenation: "a" + "b" | No | Joining dynamic values, though unnecessary for adjacent literals |
Interpolation: "Hello, #{name}" | No by itself | Inserting a value into text |
| Backslash continuation | No | Rarely useful; can make indentation harder to read |
Regular heredoc: <<~TEXT | Usually yes | Text where line breaks are meaningful |
| Multiline quoted string | Yes | Text where an embedded newline is desired |
A squiggly heredoc (<<~TEXT) removes common indentation, but it does not generally remove intentional line breaks between lines. It solves an indentation problem, not the same no-newline requirement as adjacent literals.
Cheat Sheet
# Wrap a static string with no newline added
text = "First part "
"second part"
# Equivalent value
text = "First part second part"
# Interpolation in one piece
text = "Hello, #{name}. "
"Welcome back."
# Source indentation is ignored
if enabled
text = "One "
"two"
end
- Adjacent quoted literals are joined automatically.
- Ruby adds no spaces or newlines between them.
- Include spaces and punctuation inside the literals deliberately.
- Prefer this for static text split solely for source readability.
- Use a heredoc when the resulting string should contain line breaks.
- Use parameter binding rather than interpolation for untrusted SQL values.
FAQ
Does Ruby add a space between adjacent string literals?
No. Ruby joins them exactly, so add a space inside one literal when needed.
Can I split a Ruby string across lines without using +?
Yes. Place adjacent string literals on separate lines:
text = "one "
"two"
Does indentation before the second string become part of the string?
No. Only characters inside quotes are part of the string.
Is adjacent string literal concatenation faster than using +?
For fixed literals, Ruby can handle adjacent literals as part of parsing rather than performing the same kind of runtime concatenation as String#+. Choose this form primarily because it clearly represents static text; benchmark real hot paths if performance matters.
Should I use a heredoc for a long one-line message?
Usually no. A heredoc is better when line breaks are intended in the output. Adjacent literals are clearer when the output must be one line.
Can adjacent literals contain interpolation?
Yes. Use double-quoted literals for pieces that need interpolation:
"Hello, #{name}. " "Welcome!"
Does a squiggly heredoc remove all newlines?
No. <<~ removes common leading indentation. Newlines separating heredoc lines remain part of the text unless you explicitly handle them.
Mini Project
Description
Create a small Ruby notification formatter for a Rails-style application. It produces a readable, single-line account message while the Ruby source remains within a reasonable line length. The project demonstrates adjacent string literals and interpolation without introducing unwanted newline characters.
Goal
Build a method that returns a one-line notification message for a user and plan name.
Requirements
Create a subscription_message method that accepts a user name and plan name.
Return a message that includes both values.
Wrap the static message across multiple Ruby source lines.
Ensure the returned string contains no newline characters.
Include a short example that prints the result.
Keep learning
Related questions
Add a Default Value to an Existing Column in a Rails Migration
Learn how to add or change a default value on an existing database column using Rails Active Record migrations safely.
Calling a Class Method from an Instance in Ruby
Learn how to call a class method from an instance in Ruby using self.class, with examples, pitfalls, and practical usage patterns.
Calling an Overridden Monkey-Patched Method in Ruby
Learn how to call the original method when monkey patching in Ruby, including alias_method patterns, examples, pitfalls, and practical usage.