りんごとバナナとエンジニア

エンジニア修行の記録

【Rails】devise_mailで送信元メールアドレスを動的に変える

Railsでdeviseを使ってメール送信機能を作るとき、送信元のアドレスは通常いつも同じで済むことが多い。
セオリーなのは、 config/initializers/devise.rb にある config.mailer_sender の項目に送信元アドレスを書くことだろう。

# ==> Mailer Configuration
# Configure the e-mail address which will be shown in Devise::Mailer,
# note that it will be overwritten if you use your own mailer class
# with default "from" parameter.
config.mailer_sender = 'from@example.com'

また、Mailerの種類によって異なる役割のメールアドレスを送りたいときなどは、各Mailerクラスの冒頭に default をつけることで、そのMailerで使われる送信元アドレスを上書きできる。

class UserMailer < ApplicationMailer
  default from: "info@udomomo.com"
  ...
end

しかし先日、同じMailerによるメールでも送信元アドレスを出し分ける必要が生じた。そのサービスではユーザが使うドメインによってテンプレートを出し分けており、送信元アドレスもドメインによって出し分けたかったのだ。 これをdeviseの機能でどう実現すれば良いかしばらく悩んだが、 devise_mail メソッドに opt という引数があり、それを使うことで解決できた。

# mail_address変数に送信元アドレスを入れておく
opt.store("from": mail_address)
devise_mail(record, :mail_title, opt)

この方法は、devise_mailの実装を見ることで理解できる。

github.com

def devise_mail(record, action, opts = {}, &block)
    initialize_from_record(record)
    mail headers_for(action, opts), &block
end

...

def headers_for(action, opts)
    headers = {
        subject: subject_for(action),
        to: resource.email,
        from: mailer_sender(devise_mapping),
        reply_to: mailer_reply_to(devise_mapping),
        template_path: template_paths,
        template_name: action
    }.merge(opts)

    @email = headers[:to]
    headers
end

devise_mail は、メールヘッダの設定を行ったうえでActionMailerの mail メソッドを使ってメールを送信しているが、その際 opt 引数に渡されたハッシュを merge している。このmerge とは2つのhashを合成するためのメソッドで、キーが重複した場合デフォルトでは後から指定した値で上書きされる。つまり、上書きしたいheaderの項目を opt 内で指定すれば、 devise_mail を呼び出すタイミングで設定を変えることができる。

docs.ruby-lang.org

RailsのMailerはあまり使い慣れていないので、もっとスマートな方法があれば知りたい。