rails-mailers — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited rails-mailers (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
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.
<when-to-use>
</when-to-use>
<benefits>
</benefits>
<verification-checklist> Before completing mailer work:
</verification-checklist>
<standards>
</standards>
<pattern name="actionmailer-basic-setup"> <description>Configure ActionMailer for email delivery</description>
Mailer Class:
# app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
default from: "[email protected]"
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
endHTML 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. </pattern>
<antipattern> <description>Using deliver_now in production (blocks HTTP request)</description> <bad-example>
# ❌ WRONG - Blocks HTTP request thread
def create
@user = User.create!(user_params)
NotificationMailer.welcome_email(@user).deliver_now # Blocks!
redirect_to @user
end</bad-example> <good-example>
# ✅ CORRECT - Async delivery via SolidQueue
def create
@user = User.create!(user_params)
NotificationMailer.welcome_email(@user).deliver_later # Non-blocking
redirect_to @user
end</good-example>
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. </antipattern>
<pattern name="parameterized-mailers"> <description>Use .with() to pass parameters cleanly to mailers</description>
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_laterWhy: Cleaner syntax, easier to read and modify, and works seamlessly with background jobs. </pattern>
<pattern name="email-layouts"> <description>Shared layouts for consistent email branding</description>
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>© 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. </pattern>
<pattern name="email-attachments"> <description>Attach files to emails (PDFs, CSVs, images)</description>
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
endIn 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. </pattern>
<antipattern> <description>Using _path helpers instead of _url in emails (broken links)</description> <bad-example>
# ❌ 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</bad-example> <good-example>
# ✅ 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" }</good-example>
Why bad: Emails are viewed outside your application context, so relative paths don't work. Always use *_url helpers to generate absolute URLs. </antipattern>
<pattern name="letter-opener-setup"> <description>Preview emails in browser during development without sending</description>
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. </pattern>
<pattern name="mailer-previews"> <description>Preview all email variations at /rails/mailers</description>
# 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: "[email protected]")
NotificationMailer.welcome_email(user)
end
def password_reset
user = User.first || User.new(name: "Test User", email: "[email protected]")
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: "[email protected]")
NotificationMailer.welcome_email(user)
end
endWhy: Mailer previews at /rails/mailers let you see all email variations without sending. Test different edge cases (long names, missing data, etc.). </pattern>
<pattern name="mailer-testing"> <description>Test email delivery and content with ActionMailer::TestCase</description>
# 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 ["[email protected]"], 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
endWhy: Test email delivery, content, and background job enqueuing. Verify recipients, subjects, and that emails are queued properly. </pattern>
<pattern name="environment-configuration"> <description>Configure ActionMailer for each environment</description>
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. </pattern>
<testing>
# 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 ["[email protected]"], 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: "[email protected]"
fill_in "Password", with: "password"
click_button "Sign Up"
assert_enqueued_emails 1
perform_enqueued_jobs
email = ActionMailer::Base.deliveries.last
assert_equal ["[email protected]"], email.to
assert_match "Welcome", email.subject
end
end</testing>
<related-skills>
</related-skills>
<resources>
Official Documentation:
Gems & Libraries:
Tools:
Email Service Providers:
</resources>
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.