From 2705feea05bd9b8768b6f6f39b419fa057cfbad1 Mon Sep 17 00:00:00 2001 From: Alex Castillo Date: Tue, 8 Sep 2026 15:06:01 -0400 Subject: [PATCH] Don't raise when a layout is set for only one of html/text `Layouts#layouts` maps `[:html, :text]` through `to_h { ... }` and `next`s past a format that has no layout. A `next` inside a `to_h` block yields `nil`, so `to_h` raises `TypeError: wrong element type NilClass`, and the email blows up in its constructor. Every `layout` example in the README sets a single format (`layout text:`, `layout html:`), so this is the common case, not an edge one. `filter_map { ... }.to_h` drops the missing format instead. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01GtYgwYNfJ3wHrw9xrooVoB --- lib/courrier/email/layouts.rb | 4 ++-- test/courrier/email/layouts_test.rb | 20 +++++++++++++++++++ .../test_email_with_single_format_layouts.rb | 13 ++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/test_email_with_single_format_layouts.rb diff --git a/lib/courrier/email/layouts.rb b/lib/courrier/email/layouts.rb index 51b5752..9c2cb42 100644 --- a/lib/courrier/email/layouts.rb +++ b/lib/courrier/email/layouts.rb @@ -20,13 +20,13 @@ def build def no_layouts? = @email.class.layouts.nil? def layouts - FORMATS.map(&:to_sym).to_h do |format| + FORMATS.map(&:to_sym).filter_map do |format| template = @email.class.layouts[format] next if template.nil? [format, render(template)] - end + end.to_h end def render(template) diff --git a/test/courrier/email/layouts_test.rb b/test/courrier/email/layouts_test.rb index 6074745..f3553ba 100644 --- a/test/courrier/email/layouts_test.rb +++ b/test/courrier/email/layouts_test.rb @@ -31,4 +31,24 @@ def test_mixed_layouts assert_equal expected, Courrier::Email::Layouts.new(email).build end + + def test_html_only_layout + email = TestEmailWithHtmlOnlyLayout.new( + from: "devs@railsdesigner.com", + to: "recipient@railsdesigner.com" + ) + + assert_equal [{ html: "%{content}" }], Courrier::Email::Layouts.new(email).build + assert_equal "

Body

", email.options.html + end + + def test_text_only_layout + email = TestEmailWithTextOnlyLayout.new( + from: "devs@railsdesigner.com", + to: "recipient@railsdesigner.com" + ) + + assert_equal [{ text: "%{content}\n\nThanks!" }], Courrier::Email::Layouts.new(email).build + assert_equal "Body\n\nThanks!", email.options.text + end end diff --git a/test/fixtures/test_email_with_single_format_layouts.rb b/test/fixtures/test_email_with_single_format_layouts.rb new file mode 100644 index 0000000..4438d83 --- /dev/null +++ b/test/fixtures/test_email_with_single_format_layouts.rb @@ -0,0 +1,13 @@ +require "courrier/email" + +class TestEmailWithHtmlOnlyLayout < Courrier::Email + layout html: "%{content}" + + def html = "

Body

" +end + +class TestEmailWithTextOnlyLayout < Courrier::Email + layout text: "%{content}\n\nThanks!" + + def text = "Body" +end