rails-mailers

Email with ActionMailer

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "rails-mailers" with this command: npx skills add shoebtamboli/rails_claude_skills/shoebtamboli-rails-claude-skills-rails-mailers

Email with ActionMailer

Send transactional and notification emails using ActionMailer, integrated with SolidQueue for async delivery. Create HTML and text templates, preview emails in development, and test thoroughly.

ActionMailer Setup

Mailer Class:

app/mailers/application_mailer.rb

class ApplicationMailer < ActionMailer::Base default from: "noreply@example.com" layout "mailer" end

app/mailers/notification_mailer.rb

class NotificationMailer < ApplicationMailer def welcome_email(user) @user = user @login_url = login_url mail(to: user.email, subject: "Welcome to Our App") end

def password_reset(user) @user = user @reset_url = password_reset_url(user.reset_token) mail(to: user.email, subject: "Password Reset Instructions") end end

HTML Template:

<%# app/views/notification_mailer/welcome_email.html.erb %> <h1>Welcome, <%= @user.name %>!</h1> <p>Thanks for signing up. Get started by logging in:</p> <%= link_to "Login Now", @login_url, class: "button" %>

Text Template:

<%# app/views/notification_mailer/welcome_email.text.erb %> Welcome, <%= @user.name %>!

Thanks for signing up. Get started by logging in: <%= @login_url %>

Usage (Async with SolidQueue):

In controller or service

NotificationMailer.welcome_email(@user).deliver_later NotificationMailer.password_reset(@user).deliver_later(queue: :mailers)

Why: ActionMailer integrates seamlessly with SolidQueue for async delivery. Always use deliver_later to avoid blocking requests. Provide both HTML and text versions for compatibility.

❌ WRONG - Blocks HTTP request thread

def create @user = User.create!(user_params) NotificationMailer.welcome_email(@user).deliver_now # Blocks! redirect_to @user end

✅ CORRECT - Async delivery via SolidQueue

def create @user = User.create!(user_params) NotificationMailer.welcome_email(@user).deliver_later # Non-blocking redirect_to @user end

Why bad: deliver_now blocks the HTTP request until SMTP completes, creating slow response times and poor user experience. deliver_later uses SolidQueue to send email in background.

class NotificationMailer < ApplicationMailer def custom_notification @user = params[:user] @message = params[:message] mail(to: @user.email, subject: params[:subject]) end end

Usage

NotificationMailer.with( user: user, message: "Update available", subject: "System Alert" ).custom_notification.deliver_later

Why: Cleaner syntax, easier to read and modify, and works seamlessly with background jobs.

Email Templates

HTML Layout:

<%# app/views/layouts/mailer.html.erb %> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; color: #333; } .header { background-color: #4F46E5; color: white; padding: 20px; text-align: center; } .content { padding: 20px; } .button { display: inline-block; padding: 12px 24px; background-color: #4F46E5; color: white; text-decoration: none; border-radius: 4px; } .footer { padding: 20px; text-align: center; font-size: 12px; color: #666; } </style> </head> <body> <div class="header"> <h1>Your App</h1> </div> <div class="content"> <%= yield %> </div> <div class="footer"> <p>&copy; 2025 Your Company. All rights reserved.</p> </div> </body> </html>

Text Layout:

<%# app/views/layouts/mailer.text.erb %>

YOUR APP

<%= yield %>


© 2025 Your Company. All rights reserved.

Why: Consistent branding across all emails. Inline CSS ensures styling works across email clients.

class ReportMailer < ApplicationMailer def monthly_report(user, data) @user = user

# Regular attachment
attachments["report.pdf"] = {
  mime_type: "application/pdf",
  content: generate_pdf(data)
}

# Inline attachment (for embedding in email body)
attachments.inline["logo.png"] = File.read(
  Rails.root.join("app/assets/images/logo.png")
)

mail(to: user.email, subject: "Monthly Report")

end end

In template:

<%# Reference inline attachment %> <%= image_tag attachments["logo.png"].url %>

Why: Attach reports, exports, or inline images. Inline attachments can be referenced in email body with image_tag.

❌ WRONG - Relative path doesn't work in emails

def welcome_email(user) @user = user @login_url = login_path # => "/login" (relative path) mail(to: user.email, subject: "Welcome") end

✅ CORRECT - Full URL works in emails

def welcome_email(user) @user = user @login_url = login_url # => "https://example.com/login" (absolute URL) mail(to: user.email, subject: "Welcome") end

Required configuration

config/environments/production.rb

config.action_mailer.default_url_options = { host: "example.com", protocol: "https" }

Why bad: Emails are viewed outside your application context, so relative paths don't work. Always use *_url helpers to generate absolute URLs.

Email Testing

Configuration:

Gemfile

group :development do gem "letter_opener" end

config/environments/development.rb

config.action_mailer.delivery_method = :letter_opener config.action_mailer.default_url_options = { host: "localhost", port: 3000 }

config/environments/production.rb

config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { address: "smtp.sendgrid.net", port: 587, user_name: Rails.application.credentials.dig(:smtp, :username), password: Rails.application.credentials.dig(:smtp, :password), authentication: :plain, enable_starttls_auto: true } config.action_mailer.default_url_options = { host: "example.com", protocol: "https" }

Why: letter_opener opens emails in browser during development - no SMTP setup needed. Test email appearance without actually sending.

test/mailers/previews/notification_mailer_preview.rb

class NotificationMailerPreview < ActionMailer::Preview

Preview at http://localhost:3000/rails/mailers/notification_mailer/welcome_email

def welcome_email user = User.first || User.new(name: "Test User", email: "test@example.com") NotificationMailer.welcome_email(user) end

def password_reset user = User.first || User.new(name: "Test User", email: "test@example.com") user.reset_token = "sample_token_123" NotificationMailer.password_reset(user) end

Preview with different data

def welcome_email_long_name user = User.new(name: "Christopher Alexander Montgomery III", email: "long@example.com") NotificationMailer.welcome_email(user) end end

Why: Mailer previews at /rails/mailers let you see all email variations without sending. Test different edge cases (long names, missing data, etc.).

test/mailers/notification_mailer_test.rb

class NotificationMailerTest < ActionMailer::TestCase test "welcome_email sends with correct attributes" do user = users(:alice) email = NotificationMailer.welcome_email(user)

# Test delivery
assert_emails 1 do
  email.deliver_now
end

# Test attributes
assert_equal [user.email], email.to
assert_equal ["noreply@example.com"], email.from
assert_equal "Welcome to Our App", email.subject

# Test content
assert_includes email.html_part.body.to_s, user.name
assert_includes email.text_part.body.to_s, user.name
assert_includes email.html_part.body.to_s, "Login Now"

end

test "delivers via background job" do user = users(:alice)

assert_enqueued_with(job: ActionMailer::MailDeliveryJob, queue: "mailers") do
  NotificationMailer.welcome_email(user).deliver_later(queue: :mailers)
end

end

test "password_reset includes reset link" do user = users(:alice) user.update!(reset_token: "test_token_123") email = NotificationMailer.password_reset(user)

assert_includes email.html_part.body.to_s, "test_token_123"
assert_includes email.html_part.body.to_s, "password_reset"

end end

Why: Test email delivery, content, and background job enqueuing. Verify recipients, subjects, and that emails are queued properly.

Email Configuration

Development:

config/environments/development.rb

config.action_mailer.delivery_method = :letter_opener config.action_mailer.perform_deliveries = true config.action_mailer.raise_delivery_errors = true config.action_mailer.default_url_options = { host: "localhost", port: 3000 }

Test:

config/environments/test.rb

config.action_mailer.delivery_method = :test config.action_mailer.default_url_options = { host: "example.com" }

Production:

config/environments/production.rb

config.action_mailer.delivery_method = :smtp config.action_mailer.perform_deliveries = true config.action_mailer.raise_delivery_errors = false config.action_mailer.default_url_options = { host: ENV["APP_HOST"], protocol: "https" }

config.action_mailer.smtp_settings = { address: ENV["SMTP_ADDRESS"], port: ENV["SMTP_PORT"], user_name: Rails.application.credentials.dig(:smtp, :username), password: Rails.application.credentials.dig(:smtp, :password), authentication: :plain, enable_starttls_auto: true }

Why: Different configurations per environment. Development previews in browser, test stores emails in memory, production sends via SMTP.

test/mailers/notification_mailer_test.rb

class NotificationMailerTest < ActionMailer::TestCase setup do @user = users(:alice) end

test "welcome_email" do email = NotificationMailer.welcome_email(@user)

assert_emails 1 { email.deliver_now }
assert_equal [@user.email], email.to
assert_equal ["noreply@example.com"], email.from
assert_match @user.name, email.html_part.body.to_s
assert_match @user.name, email.text_part.body.to_s

end

test "enqueues for async delivery" do assert_enqueued_with(job: ActionMailer::MailDeliveryJob) do NotificationMailer.welcome_email(@user).deliver_later end end

test "uses correct queue" do assert_enqueued_with(job: ActionMailer::MailDeliveryJob, queue: "mailers") do NotificationMailer.welcome_email(@user).deliver_later(queue: :mailers) end end end

test/system/email_delivery_test.rb

class EmailDeliveryTest < ApplicationSystemTestCase test "sends welcome email after signup" do visit signup_path fill_in "Email", with: "new@example.com" fill_in "Password", with: "password" click_button "Sign Up"

assert_enqueued_emails 1
perform_enqueued_jobs

email = ActionMailer::Base.deliveries.last
assert_equal ["new@example.com"], email.to
assert_match "Welcome", email.subject

end end

Official Documentation:

  • Rails Guides - Action Mailer Basics

Gems & Libraries:

  • letter_opener - Preview emails in browser during development

Tools:

  • Email on Acid - Email testing across clients

Email Service Providers:

  • SendGrid Rails Guide

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

rails-debugging

No summary provided by upstream source.

Repository SourceNeeds Review
General

rails-controllers

No summary provided by upstream source.

Repository SourceNeeds Review
General

rails-pagination-kaminari

No summary provided by upstream source.

Repository SourceNeeds Review
General

rspec-testing

No summary provided by upstream source.

Repository SourceNeeds Review