Agent skill
rails-mailer-patterns
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.
Install this agent skill to your Project
npx add-skill https://github.com/ag0os/rails-dev-plugin/tree/main/skills/rails-mailer-patterns
SKILL.md
Rails Mailer Patterns
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.
See patterns.md for detailed code examples.
Quick Reference
| 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 |
| Profile-aware testing | Minitest (omakase) vs RSpec (service-oriented) |
Core Principles
- Always
deliver_later: Email delivery is I/O — background job by default - Mailers are views, not logic: Assemble data and call
mail(). No business logic - One email per method: No conditionals choosing between templates
- Preview every email: Mailer previews catch layout issues CI cannot
_later/_nowon the model: The model defines when to send; the mailer defines what to send
Parameterized Mailers
Share 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
ApplicationMailer with Dynamic From
class ApplicationMailer < ActionMailer::Base
default from: -> { "#{Current.account&.name || 'App'} <noreply@example.com>" }
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
Staging Email Interceptor
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 = ["staging-inbox@example.com"]
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
Previews
# test/mailers/previews/order_mailer_preview.rb (Omakase)
# spec/mailers/previews/order_mailer_preview.rb (Service-oriented)
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
Profile-Aware Delivery Triggers
Omakase — model triggers delivery:
class Order < ApplicationRecord
after_create_commit :send_confirmation
private
def send_confirmation
OrderMailer.confirmation(self).deliver_later
end
end
Service-oriented — service triggers delivery:
class Orders::Create
def call
order = Order.create!(params)
OrderMailer.confirmation(order).deliver_later
Result.new(success: true, order: order)
end
end
Profile-Aware Testing
Omakase — Minitest:
class OrderMailerTest < ActionMailer::TestCase
test "confirmation email" do
order = orders(:confirmed)
email = OrderMailer.confirmation(order)
assert_emails 1 do
email.deliver_now
end
assert_equal [order.user.email], email.to
assert_match "Order ##{order.number}", email.subject
end
end
Service-oriented — RSpec:
RSpec.describe OrderMailer, type: :mailer do
describe "#confirmation" do
let(:order) { create(:order) }
let(:mail) { described_class.confirmation(order) }
it "renders the headers" do
expect(mail.to).to eq([order.user.email])
expect(mail.subject).to match(/Order ##{order.number}/)
end
it "renders the body" do
expect(mail.body.encoded).to include(order.number)
end
end
end
Anti-Patterns
| 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 |
Output Format
When creating mailers, provide:
- Mailer class with proper structure
- View templates (HTML + text parts)
- Preview class for visual testing
- Test file matching project profile (Minitest or RSpec)
- Delivery configuration notes if relevant
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
rails-caching-patterns
Caching patterns for Rails applications including fragment caching, low-level caching, HTTP caching, Russian doll caching, and cache invalidation strategies. Automatically invoked when working with Rails.cache, cache stores, stale?/fresh_when, fragment caching, cache keys, or performance optimization through caching. Triggers on "cache", "caching", "Rails.cache", "fragment cache", "Russian doll", "stale?", "fresh_when", "cache key", "cache store", "Redis cache", "Solid Cache", "memcached", "ETag", "cache invalidation", "cache bust". NOT for CDN configuration (use rails-devops-patterns) or database query optimization (use rails-model-patterns).
rails-graphql-patterns
Analyzes and recommends GraphQL patterns for Rails using graphql-ruby including schema design, types, resolvers, mutations, subscriptions, DataLoader, and query complexity. Use when building GraphQL APIs, defining types, writing mutations, optimizing N+1 queries, or structuring app/graphql. NOT for REST API controllers, ActiveRecord queries outside GraphQL, or Turbo Stream responses.
ruby-object-design
Automatically invoked when making decisions about Ruby code structure and organization. Triggers on "class or module", "should this be a class", "struct vs class", "PORO", "data object", "design pattern", "class vs module", "when to use class", "module vs class", "stateless class", "value object", "data container", "object factory", "extend self", "singleton class". Provides guidance on choosing the right Ruby construct (class, module, Struct, Data, Hash). NOT for code smell identification or refactoring (use ruby-refactoring) or Rails-specific framework patterns.
rails-views-patterns
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.
rails-architecture-patterns
Provides architectural planning, design decisions, and coordination guidance for Rails applications. Use when planning new features, choosing between design approaches (STI vs polymorphic, service vs concern, monolith vs engine), evaluating system architecture, or deciding which domain skill or agent to delegate to. NOT for implementation details within a single domain (use the domain-specific skill instead).
rails-devops-patterns
Analyzes Rails deployment, infrastructure, and production configuration for best practices. Use when setting up Docker, writing CI/CD pipelines, configuring Puma, adding monitoring or logging, hardening security (SSL, Rack::Attack, headers), optimizing database config, or reviewing production environment files. NOT for application business logic, model design, or test writing.
Didn't find tool you were looking for?