skills/rails-mailer-patterns/SKILL.md
Action Mailer patterns for Rails applications. Automatically invoked when working with email delivery, mailer classes, email templates, mailer previews, interceptors, or delivery configuration. Triggers on "mailer", "email", "ActionMailer", "deliver_later", "deliver_now", "mail template", "email preview", "SMTP", "SendGrid", "Postmark", "notification email". NOT for push notifications, SMS, or in-app messaging.
npx skillsauth add ag0os/rails-dev-plugin rails-mailer-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Analyze and recommend Action Mailer patterns for reliable, well-structured email delivery in Rails applications.
Follow standard Rails conventions for basic mailer structure, mail() calls, delivery configuration, multipart templates, attachments, and I18n subjects. Focus on the opinionated patterns below.
What triggers email delivery forks on Axis A — logic placement (rails-stack-profiles). Resolve it (or reuse the session value), then read the matching delivery-trigger guide above. native if it cannot be resolved.
| Pattern | Use When |
|---------|----------|
| Parameterized mailer with before_action | Shared context across multiple mailer methods |
| Mailer preview per method | Every mailer method needs a preview — catches layout issues CI cannot |
| Staging interceptor | Prevent real emails in non-production environments |
| _later/_now convention | Model triggers delivery; mailer is just the template layer |
deliver_later: Email delivery is I/O — background job by defaultmail(). No business logic_later/_now on the model: The model defines when to send; the mailer defines what to sendShare context across methods without repeating arguments:
class UserMailer < ApplicationMailer
before_action { @user = params[:user] }
before_action { @account = params[:user].account }
default to: -> { @user.email }
def welcome
mail(subject: "Welcome to #{@account.name}")
end
def weekly_digest
@events = @user.events.from_last_week
mail(subject: "Your weekly digest")
end
end
# Usage
UserMailer.with(user: user).welcome.deliver_later
class ApplicationMailer < ActionMailer::Base
default from: -> { "#{Current.account&.name || 'App'} <[email protected]>" }
layout "mailer"
rescue_from Net::SMTPSyntaxError, with: :log_delivery_error
private
def log_delivery_error(exception)
Rails.logger.error("[Mailer] Delivery failed: #{exception.message}")
end
end
Prevent sending real emails in staging — redirects all mail to a safe inbox:
# app/mailers/interceptors/staging_interceptor.rb
class StagingInterceptor
def self.delivering_email(message)
original_to = message.to
message.to = ["[email protected]"]
message.cc = nil
message.bcc = nil
message.subject = "[STAGING] #{message.subject} (was: #{original_to.join(', ')})"
end
end
# config/initializers/mail_interceptors.rb
if Rails.env.staging?
ActionMailer::Base.register_interceptor(StagingInterceptor)
end
# Preview lives under the project's test directory:
# test/mailers/previews/ or spec/mailers/previews/
class OrderMailerPreview < ActionMailer::Preview
def confirmation
order = Order.first || Order.new(id: 1, number: "PREVIEW-001", created_at: Time.current)
OrderMailer.confirmation(order)
end
def confirmation_with_discount
order = Order.joins(:discount).first
OrderMailer.confirmation(order)
end
end
# Visit: http://localhost:3000/rails/mailers/order_mailer/confirmation
What triggers deliver_later depends on Axis A — see delivery-trigger.native.md or delivery-trigger.extracted.md.
Assert the same things regardless of framework: the email enqueues (or sends), the recipients and subject are correct, and the body includes the expected content. ActionMailer::TestCase / assert_emails and have_enqueued_mail are the core tools.
Write the test in the project's framework — read it from the project-conventions fingerprint (Testing category). Framework-specific patterns belong to rails-testing-patterns; do not infer the framework from architecture.
| Anti-Pattern | Fix |
|-------------|-----|
| deliver_now in controllers | Use deliver_later — email is I/O |
| Business logic in mailers | Move to model/service, pass data to mailer |
| Conditional templates in one method | One method per email |
| No previews | Add preview for every mailer method |
| HTML-only emails | Always include text part |
When creating mailers, provide:
project-conventions fingerprint)development
WHAT: Language-agnostic corrective guidance for the refactoring phase. WHEN: Agent is restructuring code, fixing code smells, reducing complexity, or improving maintainability. NOT FOR: Writing new features, debugging runtime errors, performance tuning, or object design decisions.
tools
Analyzes Rails view templates, partials, layouts, helpers, and form patterns for best practices. Use when reviewing ERB templates, improving view performance with fragment caching, fixing form helpers, organizing partials, adding accessibility attributes, or evaluating collection rendering. NOT for Stimulus/Turbo logic (use hotwire-patterns), controller concerns, or API-only responses.
testing
Analyzes Rails test suites and recommends testing best practices for RSpec and Minitest. Use when writing new tests, reviewing test coverage, fixing flaky tests, improving test performance, choosing between test types (unit, integration, system, request), or setting up factories and fixtures. NOT for production monitoring, deployment verification, or load/stress testing infrastructure.
development
Detects a Rails project's architecture axes — logic placement (native vs extracted) and delivery (html vs api) — so other skills load profile-appropriate guidance without inline conditionals. Use when planning architecture or when a recommendation depends on where business logic lives or whether the app renders HTML or serves JSON. NOT for test framework, job backend, cache store, or auth library choices — those are orthogonal facts detected by project-conventions.