In Ruby, how do you split a long method chain across lines so it stays one expression, and where does a block attach?
answer
- a complete line ends the expression
- a line starting with a dot continues
- comments allowed between since 2.7
- block binds to the call just before it
- Layout/DotPosition defaults to leading
basics
~20 sStart each continuation line with a dot, or end the previous line with one; either tells the parser the expression continues. A block attaches to the call written just before it, and the chain may continue after its closing brace or end.
solid answer
~50 sRuby ends an expression at a newline whenever the line is already complete, so `titles.map(&:strip)` followed by a line `reject(&:empty?)` is two statements. Two layouts keep the chain together: a **trailing dot** at the end of a line, which leaves it incomplete, or a **leading dot** at the start of the next line, which the parser treats as a continuation. Leading dots are the common style and RuboCop's `Layout/DotPosition` default; since Ruby 2.7, comment lines may sit between them, and Ruby 4.0 extends the same rule to lines starting with `&&`, `||`, `and` or `or`. A block belongs to the method call right before it: in `.map { |t| t[0, 60] }.join(", ")` the block goes to `map` and `join` runs on the result. RuboCop's `Style/MultilineBlockChain` flags chaining a further block onto a multi-line `do...end` block.
code
ruby · 10 linesraw_titles = [" Red mug ", "", " Blue cup"]
labels = raw_titles
.map(&:strip)
# blank rows come from the import
.reject(&:empty?)
.map { |t| t.squeeze(" ") }
.join(", ")
labels # => "Red mug, Blue cup"go deeper
Recall the leading-dot layout for long chains and that a block belongs to the call right before it.
Explain why a newline after a complete line ends the expression, why a leading dot overrides that, and how comment lines and backslashes behave.
Keep long transformation chains reviewable: one step per line, comments where a step needs a reason, and named locals instead of chaining off multi-line blocks.
Choose and enforce a codebase-wide layout for chains, including the dot position and the rule for multi-line block chains, so diffs stay small and readable.
## When a newline ends an expression Ruby treats a newline as the end of a statement **when the line is already a complete expression**. A line that is visibly unfinished, ending in `+`, `,`, `=` or `.`, continues onto the next one. That is why this is two statements, not one chain: ```ruby titles.map(&:strip) # complete: a statement on its own reject(&:empty?) # a separate call on self, not on the mapped Array ``` The second line calls `reject` on `self`, which usually raises `NoMethodError` or, worse, calls some unrelated method. ## Two layouts that keep a chain together | Style | Looks like | Why it parses | |---|---|---| | Trailing dot | `titles.` at line end, `map(&:strip).` next | the line ends with `.` and is incomplete | | Leading dot | `titles` then `.map(&:strip)` on the next line | a line starting with `.` continues the previous expression | | Backslash | `titles \` then `.map(&:strip)` | `\` explicitly joins the lines at parse time | - The **leading dot** is the common style: every step starts on its own line and can be read, reordered or commented out individually. It is the default of RuboCop's `Layout/DotPosition` cop (`EnforcedStyle: leading`), and `Layout/MultilineMethodCallIndentation` checks how the continued lines are indented. - Since **Ruby 2.7**, **comment lines** may sit between leading-dot lines, so a step can be annotated or disabled with `#`. - After a backslash no comment line is allowed, because the backslash is a parse-time line-break escape: the very next physical line must be the continuation. - **Ruby 4.0** extends the leading-line rule to logical operators: a line starting with `&&`, `||`, `and` or `or` now continues the previous line, just as a leading dot does. ```ruby display_titles = raw_titles .map(&:strip) # drop blank rows from the CSV import .reject(&:empty?) .map { |t| t.squeeze(" ") } .first(20) ``` ## Where a block goes in a chain A block is not a separate link: it is part of **one** method call, and it belongs to the call written immediately before it. 1. In `titles.map { |t| t.strip }.join(", ")`, the block is `map`'s; `join` then runs on the Array `map` returned. 2. To give a block to an earlier link, write it on that link: `titles.select { |t| t.size > 3 }.map(&:upcase)`. 3. The chain may continue after the closing `}` or `end`: `.map { ... }.join(", ")` or `end.join(", ")`. Chaining onto a multi-line `do...end` block, as in `end.map do ... end`, is legal but hard to scan. RuboCop's `Style/MultilineBlockChain` flags chaining a block after another block that spans multiple lines and suggests naming the intermediate result instead. How `{}` and `do...end` differ when a call's parentheses are omitted is a separate precedence question about blocks. ## Common mistakes - Adding a comment after a backslash continuation, which breaks the join because the backslash needs the continuation on the very next line. - Mixing leading and trailing dots in one chain, which linters reject and readers misread. - Assuming a block written at the end of a long chain applies to the first call. ## Choosing where to break A chain does not have to be split at every dot. A practical rule is to keep short, obvious steps together and give each **meaningful transformation** its own line: - Break before each step a reader should see as a separate stage: cleaning, filtering, formatting, limiting. - Keep a step with its block on one line when the block is short, `.map { |t| t.squeeze(" ") }`; switch to a named local when the block needs several lines. - Put the receiver on the first line alone when the chain is long, so every following line starts with a dot and aligns. - When a step needs an explanation, use a comment line between links rather than a trailing comment after the code.
- Why do teams usually prefer the leading dot over the trailing dot?With a leading dot, each line starts with the method it calls, so the chain reads as a list of steps, and any single step can be commented out without touching the line above. With a trailing dot, removing the last step means editing the previous line too. RuboCop's `Layout/DotPosition` defaults to the leading style, which makes it the norm in most codebases.
- In `titles.map { |t| t.strip }.join(", ")`, what does `join` receive?The Array returned by `map`. The block is part of the `map` call, and the chain continues after the closing brace, so `join` is sent to `map`'s result, not to `titles`. The same is true after `end` for a `do...end` block, although chaining off a multi-line block is usually rewritten with a named local.
saying these in an interview costs you the question
- Any newline inside a chain ends the expression, so long chains must stay on one line.
- Comment lines between leading-dot lines are a syntax error in current Ruby.
- A block at the end of a chain is passed to the first method in the chain.
- You cannot call another method after a block's closing brace.
- Leading && and || continuation lines have always worked in Ruby.