ActionMailerのSMTP設定を動的に変更する

# この記事について

Rails からメールを送る際、ActionMailerを使ってメールを送るケースが多いと思だろう。
しかし メールを送るサーバーを動的に設定したい場合はどうすればいいかについて解説している記事は少ないため、この記事で解説する。

一般的にRailsからActionMailerを使ってSMTP経由でメールを送る場合、下記の記事が参考になる。

* [Railsガイド / Action Mailer の基礎 / Action Mailerを設定する]( https://railsguides.jp/action_mailer_basics.html#action-mailer%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B)

この場合、SMTPサーバーの設定を `config/environment.rb` や `config/environments/production.rb` に `config.action_mailer.smtp_settings = { ... }`として設定することになる。

これは、１つのアプリケーションから１つのメールサービスしか使用しない場合は問題なく動作するが、ユーザーやグループによってアプリケーションが使用するメールサーバーを変えたい場合は意図したように機能しないだろう。

この記事ではユーザーやグループによってアプリケーションが使用するメールサーバーを変えたい場合、つまりは１つのアプリケーションから複数のメールサーバーを使用する場合について１つの解法を示していく。

# 定石

Railsでメールを送る場合、[Railsガイド / Action Mailer の基礎 / Action Mailerを設定する]( https://railsguides.jp/action_mailer_basics.html#action-mailer%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B) に沿ってメールサーバーの設定を `config/environment.rb` や `config/environments/production.rb` に対して `config.action_mailer.smtp_settings = { ... }`として設定することになる。

* [SendGrid / Ruby on Railsでメール送信](https://sendgrid.kke.co.jp/docs/Integrate/Frameworks/rubyonrails.html)

```ruby:config/environment.rb 
ActionMailer::Base.smtp_settings = {
  :user_name => 'your_sendgrid_username',
  :password => 'your_sendgrid_password',
  :domain => 'yourdomain.com',
  :address => 'smtp.sendgrid.net',
  :port => 587,
  :authentication => :plain,
  :enable_starttls_auto => true
}
```

この場合はActionMailer経由の全てのメールがこのSMTP設定を用いて配信されることになる。

# カスタマイズ

しかし実際のところはユーザーやグループによっては特定のメールサーバーにSMTPで接続してメールの配信を行いたいといったケースが出てくる事が多い。

上記のように `ActionMailer::Base.smtp_settings = {}` で設定してしまう場合、適用した瞬間から次に上書きされるまでの間はそのSMTP接続を使用してメール送信を行なってしまうため、タイミング次第では意図しないSMTPサーバーの使用・・・古い言葉を使うと『混線』を起こすことになる。

これに対する１つの解法としては、ユーザーやグループといったメールサーバー毎にコンテナ化したアプリを配置することで解決する方法もあるが、今回は１つのRailsアプリ（コンテナ）で複数のメールサーバー話を使用するカスタマイズを行うやり方の１つを紹介する。

## やり方

カスタマイズといってもそう大したことをするわけではなく、 `ActionMailer::Base#mail` の引数である `delivery_method_options:`を利用する。

`delivery_method_options` については [Railsガイドで解説されている]( https://railsguides.jp/action_mailer_basics.html#%E3%83%A1%E3%83%BC%E3%83%AB%E9%80%81%E4%BF%A1%E6%99%82%E3%81%AB%E9%85%8D%E4%BF%A1%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E5%8B%95%E7%9A%84%E3%81%AB%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B)が、実際に認知されているケースは多くないように感じる。

Railsガイドでは`user_name`、`password`、`address`を上書きしている例が書かれているが、実はこの `delivery_method_options:`は同様に他の`smtp_settings`の値についても設定する事ができる

* [Actio Mailer の基礎 / Action Mailerを設定する](https://railsguides.jp/action_mailer_basics.html#action-mailer%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B)


## 実際にやってみる

はじめに、`ActionMailer::Base`モジュールに設定されたデフォルトの`smtp_settings`を取得してみる

```ruby
ActionMailer::Base.smtp_settings
```
```ruby
 => {:address=>"localhost", :port=>25, :domain=>"localhost.localdomain", :user_name=>nil, :password=>nil, :authentication=>nil, :enable_starttls_auto=>true} 
```

何も設定されない場合は上記のような設定になっている。

ここでSaaS経由でのメール送信について実行してみる。

SendGridでAPIキーを使ったメール送信を行う場合、SMTP接続設定パラメータは下記のようになる。

* [SendGrid / SMTPの設定方法](https://sendgrid.kke.co.jp/docs/API_Reference/SMTP_API/integrating_with_the_smtp_api.html)

| ActionMailerの設定項目 | 設定値               |
|:----:|:--- |
| `user_name`      | `apikey`            |
| `password`       | APIキーの値 （仮に `{APIキー}` とする）          |
| `address`        | `smtp.sendgrid.net` |
| `domain`         | メール送信元ドメイン　（仮に `{送信元ドメイン}`とする）     |
| `authentication` | `login` |
| `enable_starttls_auto` | `true` |
| `port`                 | `587`  |

これをActonMailerで動的に使用する場合、下記のように`delivery_method_options:`を使って、メール毎のSMTPサーバーの設定を上書きする。
（送信先アドレスは、仮に`{YOUR_TEST@MAIL.ADDRESS}`として設定）

```ruby
ActionMailer::Base.mail(
  to:      '{YOUR_TEST@MAIL.ADDRESS}',
  from:    'from@{送信元ドメイン}',
  subject: 'テストメール',
  body:    'メール本文',
  delivery_method_options: {
    user_name: 'apikey',
    password:  '{APIキー}',
    address:   'smtp.sendgrid.net',
    domain:    '{送信元ドメイン}',
    authentication: 'login',
    enable_starttls_auto: true,
    port: 587,
  }
).deliver_later
```

上記の例では `ActionMailer::Base` に対して上書きを行っているが、`app/mailers`配下の各クラスに対しても同様の処理を行ってメール配信を行う事ができる。


```ruby:ex）HogeNotifyが存在している場合
HogeNotify.mail(
  to:      '{YOUR_TEST@MAIL.ADDRESS}',
  from:    'from@{送信元ドメイン}',
  subject: 'テストメール',
  body:    'メール本文',
  delivery_method_options: {
    user_name: 'apikey',
    password:  '{APIキー}',
    address:   'smtp.sendgrid.net',
    domain:    '{送信元ドメイン}',
    authentication: 'login',
    enable_starttls_auto: true,
    port: 587,
  }
).deliver_later
```

また、rails consoleから確認する場合は `deliver_later` ではなく `deliver` を使う事で同期的にメールを送って確認する事ができる。

### １. consoleを起動する

```sh
bundle exec rails c
```

```
Running via Spring preloader in process 2509
Loading development environment (Rails 6.0.3.4)
```

### 2. メールを送信する

```ruby
ActionMailer::Base.mail(
  to:      '{YOUR_TEST@MAIL.ADDRESS}',
  from:    'from@{送信元ドメイン}',
  subject: 'テストメール',
  body:    'メール本文',
  delivery_method_options: {
    user_name: 'apikey',
    password:  '{APIキー}',
    address:   'smtp.sendgrid.net',
    domain:    '{送信元ドメイン}',
    authentication: 'login',
    enable_starttls_auto: true,
    port: 587,
  }
).deliver
```


# まとめ

以上のように ActionMailerが利用するメールサーバーは、 `config/environment.rb` や `config/environments/production.rb` に対して設定する以外に、`ActionMailer::Base#mail` の引数である `delivery_method_options:` を使って設定する事で、動的なSMTPサーバー設定を施す事ができる。

これはサービスを利用するユーザーやグループ単位でのメールサーバの切替等に活用できる仕組みであり、Railsの高い汎用性を表している一面であるとも言えるのではないだろうか。