action-mailer について
アプリのメールを作成・管理する開発者やコーディングエージェント向けの Rails Action Mailer スキルです。メーラー、テンプレート、配信設定、プレビュー、テスト、添付ファイルを扱います。
主な機能
- 01パラメーター化メーラーのパターン
- 02HTML とテキストのメールテンプレート
- 03キューおよび SMTP 配信設定
- 04プレビューと配信テスト
- 05添付ファイルとインライン画像
- 06コールバックとメールのローカライズ
活用シーン
- 01アカウントやパスワードのメール作成
- 02マルチパートメールテンプレートの構築
- 03環境ごとのメール配信設定
- 04メール内容のプレビューとテスト
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ thinkoodle/ rails-skills --skill action-mailerインストール後、Claude Code で/action-mailerを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/configuration.mddelivery.mdinterceptors.mdpreviews.mdtemplates.mdtesting.md詳細
Rails Action Mailer エキスパート
Rails アプリケーションで Action Mailer を使ってメールを作成、設定、送信します。
方針
基本原則:
- デフォルトで
deliver_laterを使用します — Active Job 経由でメールをキューに登録します。deliver_nowは HTTP リクエストをブロックするため、SMTP の往復処理が完了するまでユーザーを待たせます。 - HTML とテキストの両方のテンプレートを作成します — HTML をブロックするメールクライアントもあり、HTML のみのメールはスパムフィルターで不利になります。必ずテキストのフォールバックを用意してください。
- パラメーター化されたメーラーを使用します — メソッド引数ではなく、
with()経由でコンテキストを渡します。これにより、コールバックや共通のセットアップを適切に機能させられます。 default from:を設定します — すべてのメーラーにデフォルトの送信元が必要です。設定しない場合、メールの送信が通知なく失敗したり、メールサーバーに拒否されたりします。- プレビューを作成します — プレビュークラスを使うと、実際にメールを送信したり、テストデータを手作業で作成したりせずに、メールのデザインを反復して調整できます。
_pathではなく_urlヘルパーを使います — メールにはリクエストのコンテキストがないため、相対パスではリンクが壊れます。_urlは絶対 URL を生成します。
このスキルを使用する場面
- 新しいメーラークラスの生成
- メールテンプレート(HTML とテキスト)の作成
- SMTP/配信設定
- 添付ファイルやインライン画像の追加
- メーラーのプレビューとテストの作成
- インターセプターやオブザーバーのセットアップ
- メールの件名への I18n の実装
- メール配信の問題のデバッグ
手順
Step 1: 既存のメーラーパターンを確認する
まずプロジェクトを確認し、一貫性を保つため既存のメーラーパターンに合わせます:
# Find existing mailers
ls app/mailers/
# Check ApplicationMailer defaults
cat app/mailers/application_mailer.rb
# Find existing templates
ls app/views/layouts/mailer.*
find app/views -name "*.html.erb" -path "*/mailer*" -o -name "*.text.erb" -path "*/mailer*"
# Check delivery config
grep -r "action_mailer" config/environments/
# Find existing previews
ls test/mailers/previews/
理論上のベストプラクティスより、既存のコードベースとの一貫性が重要です。
Step 2: メーラーを生成または作成する
アクション名を指定してジェネレーターを使用します:
bin/rails generate mailer User welcome_email password_reset
次のファイルが作成されます:
app/mailers/user_mailer.rb— メーラークラスapp/views/user_mailer/welcome_email.html.erb— HTML テンプレートapp/views/user_mailer/welcome_email.text.erb— テキストテンプレートapp/views/user_mailer/password_reset.html.erbapp/views/user_mailer/password_reset.text.erbtest/mailers/user_mailer_test.rb— テストファイルtest/mailers/previews/user_mailer_preview.rb— プレビュークラス
ApplicationMailer が存在しない場合は、作成します:
# app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
default from: "[email protected]"
layout "mailer"
end
Step 3: メーラークラスを作成する
パラメーター化されたメーラー(with/params パターン)を使用します:
# app/mailers/user_mailer.rb
class UserMailer < ApplicationMailer
default from: "[email protected]"
def welcome_email
@user = params[:user]
@login_url = login_url
mail(
to: email_address_with_name(@user.email, @user.name),
subject: "Welcome to #{app_name}"
)
end
def password_reset
@user = params[:user]
@token = params[:token]
@reset_url = edit_password_reset_url(token: @token)
mail(to: @user.email, subject: "Reset your password")
end
private
def app_name
Rails.application.class.module_parent_name
end
end
メーラーの呼び出し — deliver_later を優先します:
# Preferred — async via Active Job (user doesn't wait for SMTP)
UserMailer.with(user: @user).welcome_email.deliver_later
# With params
UserMailer.with(user: @user, token: @token).password_reset.deliver_later
# Synchronous — blocks the request while talking to SMTP server
UserMailer.with(user: @user).welcome_email.deliver_now
deliver_now を使用するのは、rake タスク/cron ジョブ、コンソールでのデバッグ、または即時の確認が必要な重要なメールに限ります。
Step 4: 両方のテンプレートを作成する
HTML とテキストの両方のバージョンを作成します。 両方が存在すると、Action Mailer は multipart/alternative メールを自動生成します。
HTML テンプレート(app/views/user_mailer/welcome_email.html.erb):
<h1>Welcome, <%= @user.name %>!</h1>
<p>Thanks for signing up. Your account is ready.</p>
<p>
<%= link_to "Log in to your account", @login_url %>
</p>
テキストテンプレート(app/views/user_mailer/welcome_email.text.erb):
Welcome, <%= @user.name %>!
Thanks for signing up. Your account is ready.
Log in: <%= @login_url %>
両方が必要な理由 HTML をブロックするメールクライアントもあります。HTML のみのメールはスパムフィルターで不利になります。テキストのフォールバックは、メールの到達率を確保するために必要です。
Step 5: URL ホストを設定する
相対パスを解決するブラウザーリクエストのコンテキストがないため、メールには絶対 URL が必要です:
# config/environments/development.rb
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
# config/environments/production.rb
config.action_mailer.default_url_options = { host: "www.example.com", protocol: "https" }
テンプレートでは _url ヘルパーを使用します:
<%# CORRECT %>
<%= link_to "View order", order_url(@order) %>
<%# WRONG — will break in email %>
<%= link_to "View order", order_path(@order) %>
Step 6: メーラーのプレビューを作成する
メーラーの各アクションにプレビューを作成します — 実際にメールを送信せずに /rails/mailers でデザインを反復して調整できます:
# test/mailers/previews/user_mailer_preview.rb
class UserMailerPreview < ActionMailer::Preview
def welcome_email
user = User.first || User.new(name: "Preview User", email: "[email protected]")
UserMailer.with(user: user).welcome_email
end
def password_reset
user = User.first || User.new(name: "Preview User", email: "[email protected]")
UserMailer.with(user: user, token: "preview-token-123").password_reset
end
end
プレビューのヒント:
- データベースが空でもプレビューが動作するように、
User.firstとフォールバックのUser.new(...)を使用します - メールオブジェクトを返すだけにし、プレビュー内で
deliver_laterを呼び出さないでください - テンプレートの変更時にはプレビューが自動的に再読み込みされます
Step 7: メーラーのテストを作成する
# test/mailers/user_mailer_test.rb
require "test_helper"
class UserMailerTest < ActionMailer::TestCase
test "welcome_email" do
user = users(:active_user)
email = UserMailer.with(user: user).welcome_email
assert_emails 1 do
email.deliver_now
end
assert_equal ["[email protected]"], email.from
assert_equal [user.email], email.to
assert_equal "Welcome to MyApp", email.subject
# Test HTML part
assert_match user.name, email.html_part.body.to_s
assert_match "Log in", email.html_part.body.to_s
# Test text part
assert_match user.name, email.text_part.body.to_s
end
end
主なテストアサーション:
# Count emails sent
assert_emails 1 do
UserMailer.with(user: user).welcome_email.deliver_now
end
# No emails sent
assert_no_emails do
# action that shouldn't send email
end
# Enqueued for later delivery
assert_enqueued_emails 1 do
UserMailer.with(user: user).welcome_email.deliver_later
end
# Check email content
assert_equal ["[email protected]"], email.to
assert_equal ["[email protected]"], email.from
assert_equal "Subject", email.subject
assert_match "expected text", email.body.encoded
Step 8: 配信方法を設定する
開発環境 — letter_opener または :test: を使用します
# config/environments/development.rb
config.action_mailer.delivery_method = :letter_opener # Opens in browser
# OR
config.action_mailer.delivery_method = :test # Stores in ActionMailer::Base.deliveries
本番環境 — SMTP:
# config/environments/production.rb
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address: "smtp.example.com",
port: 587,
domain: "example.com",
user_name: Rails.application.credentials.dig(:smtp, :user_name),
password: Rails.application.credentials.dig(:smtp, :password),
authentication: "plain",
enable_starttls: true,
open_timeout: 5,
read_timeout: 5
}
テスト環境 — 必ず :test を使用します:
# config/environments/test.rb
config.action_mailer.delivery_method = :test
クイックリファレンス
mail() メソッドのオプション
| オプション | 説明 |
|---|---|
to: | 受信者 — 文字列または配列 |
from: | 送信者(デフォルトを上書き) |
cc: | カーボンコピーの受信者 |
bcc: | ブラインドカーボンコピーの受信者 |
reply_to: | 返信先アドレス |
subject: | メールの件名 |
headers: | カスタムヘッダーのハッシュ |
template_path: | カスタムビューのディレクトリ |
template_name: | カスタムテンプレート名 |
delivery_method_options: | メールごとの配信設定の上書き |
添付ファイル
def invoice_email
@invoice = params[:invoice]
# Simple attachment
attachments["invoice.pdf"] = File.read("/path/to/invoice.pdf")
# Attachment with options
attachments["report.csv"] = {
mime_type: "text/csv",
content: generate_csv_data
}
mail(to: @invoice.customer_email, subject: "Your Invoice")
end
インライン添付ファイル(メール本文内の画像)
# In mailer
def newsletter
attachments.inline["logo.png"] = File.read("app/assets/images/logo.png")
mail(to: params[:user].email, subject: "Newsletter")
end
<%# In HTML template %>
<%= image_tag attachments["logo.png"].url, alt: "Company Logo" %>
コールバック
class ApplicationMailer < ActionMailer::Base
before_action :set_default_vars
after_action :log_delivery
after_deliver :record_sent
private
def set_default_vars
@company_name = "MyApp"
end
def log_delivery
Rails.logger.info("Preparing email: #{action_name}")
end
def record_sent
# Called after successful delivery
EmailLog.create!(mailer: self.class.name, action: action_name)
end
end
インターセプターとオブザーバー
# config/initializers/mail_interceptors.rb
Rails.application.configure do
if Rails.env.staging?
config.action_mailer.interceptors = %w[SandboxEmailInterceptor]
end
config.action_mailer.observers = %w[EmailDeliveryObserver]
end
# app/interceptors/sandbox_email_interceptor.rb
class SandboxEmailInterceptor
def self.delivering_email(message)
message.to = ["[email protected]"]
end
end
# app/observers/email_delivery_observer.rb
class EmailDeliveryObserver
def self.delivered_email(message)
EmailDelivery.log(message)
end
end
メーラーでの rescue_from
class NotifierMailer < ApplicationMailer
rescue_from ActiveJob::DeserializationError do |exception|
# Handle stale records gracefully
end
rescue_from Net::SMTPAuthenticationError do |exception|
# Handle SMTP auth failures
end
end
メールの件名の I18n
# config/locales/en.yml
en:
user_mailer:
welcome_email:
subject: "Welcome to %{app_name}"
password_reset:
subject: "Reset your password"
# Mailer — omit subject: to auto-lookup from I18n
def welcome_email
@user = params[:user]
mail(to: @user.email) # Subject from en.user_mailer.welcome_email.subject
end
共通コンテキスト用のパラメーター化されたメーラー
複数のアクション間でコンテキストを共有するには、before_action と params を使用します。
class InvitationsMailer < ApplicationMailer
before_action { @inviter = params[:inviter]; @invitee = params[:invitee] }
default to: -> { @invitee.email },
from: -> { email_address_with_name("[email protected]", @inviter.name) }
def account_invitation
mail subject: "#{@inviter.name} invited you to #{params[:inviter].account.name}"
end
end
# Call with: InvitationsMailer.with(inviter: user, invitee: other).account_invitation.deliver_later
詳細なリファレンス
より詳しいパターンや例については、references/ ディレクトリを参照してください。
references/templates.md— メーラークラスのパターン、HTML/テキストテンプレート、添付ファイル、I18n、レイアウトreferences/delivery.md— SMTP 設定、環境ごとのセットアップ、エラー処理references/testing.md— メーラーテストのパターン、配信アサーション、統合テストreferences/interceptors.md— コールバック、インターセプター、オブザーバー、配信ライフサイクルreferences/previews.md— プレビュークラス、フォールバックデータ、カスタムプレビューパスreferences/configuration.md— エッジケース、注意点、本番環境向けレシピ
エージェントによくある間違い
deliver_laterの代わりにdeliver_nowを使用する — SMTP の応答を待つ間、HTTP リクエストをブロックするため、ユーザーはページの読み込みが遅いと感じます- HTML テンプレートだけを作成する — スパムフィルターは HTML のみのメールを低く評価し、一部のクライアントは HTML を完全にブロックします
default from:アドレスを設定し忘れる — メールサーバーは送信者のないメールを拒否します。多くの場合、通知なく拒否されます- テンプレートで
_pathヘルパーを使用する — メールクライアントでは解決に使うリクエストコンテキストがないため、相対 URL が生成され、リンクが機能しません with()を使わずに引数を渡す — パラメーター化されたメーラー(Mailer.with(user: @user).action)を使うと、コールバックや共通セットアップをすっきり扱えますdefault_url_optionsの設定を忘れる — ホスト設定がない場合、本番環境で_urlヘルパーがエラーを発生させるか、localhostへのリンクを生成します- プレビューを書かない — プレビューがないと、実際にメールを送信するか、メールの見た目を確認するためだけのテストを書くことになります
- 環境ごとに配信方法を設定しない — テストには
:test、開発には:letter_opener、本番には:smtpを使用します
避けるべきアンチパターン
- 肥大化したメーラー — ビジネスロジックはモデルやサービスに置いてください。メーラーは組み立てて送信するだけにします。メーラー内のロジックはテストや再利用が困難です
- テンプレート内のインラインスタイル — 共通スタイルを備えたメーラーレイアウトを使い、メールクライアントとの互換性のために CSS を自動でインライン化する
premailer-railsの使用を検討してください - ハードコードされた URL —
_url接尾辞のルートヘルパーはルートが変わっても正しく機能しますが、ハードコードされた文字列はすぐに古くなります - テキストのフォールバックがない — HTML のみのメールは配信スコアを下げ、スクリーンリーダーから利用できなくなります
- コントローラーでの同期配信 — リクエスト内で
deliver_nowを呼び出すと、SMTP の往復を待つ間ユーザーを待たせます - テストされていないメーラー — 内容、受信者、件名、配信数をテストしてください
- メーラービュー内のシークレット — メールでトークンやパスワードを決して公開しないでください。代わりに、有効期間が短いハッシュ化リンクを使います
ファイル構成
app/
mailers/
application_mailer.rb # Base class — default from, layout
user_mailer.rb # Mailer class
views/
layouts/
mailer.html.erb # HTML layout (wraps all mailer HTML views)
mailer.text.erb # Text layout
user_mailer/
welcome_email.html.erb # HTML template
welcome_email.text.erb # Text template
config/
environments/
development.rb # delivery_method, default_url_options
production.rb # SMTP settings
test.rb # delivery_method: :test
test/
mailers/
user_mailer_test.rb # Mailer tests
previews/
user_mailer_preview.rb # Email previews