Skip to content

Substitute the layout %{content} token literally, not through String#% - #67

Open
pcbeingused333 wants to merge 1 commit into
Rails-Designer:mainfrom
pcbeingused333:fix/literal-content-token-in-layouts
Open

pcbeingused333 wants to merge 1 commit into
Rails-Designer:mainfrom
pcbeingused333:fix/literal-content-token-in-layouts

Conversation

@pcbeingused333

Copy link
Copy Markdown
Contributor

Options#wrap inserted the email body into a layout with layout % {content: wrapped}. String#% treats every % in the layout string as a format directive, so a layout with a bare % blows up before the email is sent:

class OrderEmail < Courrier::Email
  layout html: "<table style='width:100%'><tr><td>\n%{content}\n</td></tr></table>"
end
# ArgumentError: malformed format string - %'

width: 100%, a percent-encoded URL, a line-height: 150% in an inline <style> — all of these are ordinary in an HTML email layout, and any one of them takes delivery down.

Fix

layout.gsub("%{content}") { wrapped }

%{content} is the only token the layout ever interpolated, so a literal substitution covers it. The block form of gsub (rather than gsub("%{content}", wrapped)) is deliberate: it passes the wrapped body through untouched when it contains \1, \\ or \&, which the string-replacement form would eat.

Behaviour change

A layout that wrote %% to get a literal % through String#% now keeps both characters. That escape was never documented (the README only shows %{content}), and it was the same feature that made bare % crash.

Tests

Options had no test file — the layout-wrapping path (Options#wrap) was only exercised indirectly. New test/courrier/email/options_test.rb covers the wrap, the literal %, and the backslash-sequence passthrough. With lib/courrier/email/options.rb reverted the % case errors exactly as above.

bundle exec rake test on Ruby 3.4: 162 runs, 306 assertions, 0 failures. rake standard clean.

🤖 Generated with Claude Code

`Options#wrap` inserted the body with `layout % {content: ...}`. `String#%`
reads every `%` in the layout as a format directive, so any HTML layout that
carries a bare `%` — `width: 100%`, a percent-encoded URL, an inline `<style>`
rule — raised `ArgumentError: malformed format string` and the email never
went out.

Replace it with `layout.gsub("%{content}") { wrapped }`. The block form also
keeps the wrapped body verbatim when it contains `\1`, `\\` or `\&`, which the
string form of `gsub` would consume.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant