Rails6 のちょい足しな新機能を試す27（perform_deliveries 編）

# はじめに

Rails 6 に追加されそうな新機能を試す第27段。 今回は、 `perform_deliveries` 編です。
`perform_deliveries` の値が false(nil) のとき、log への出力メッセージがメールの送信をスキップしたことを示す内容に変わりました。

今回は、User を登録するときに `perform_deliveries` の値を画面から指定できるようにする（メールを送信する、しないを指定できるようにする）ことにより、動作を確認します。

Ruby 2.6.3, Rails 6.0.0.rc1 で確認しました。Rails 6.0.0.rc1 は `gem install rails --prerelease` でインストールできます。

```shell
$  rails --version
Rails 6.0.0.rc1
```

# Rails プロジェクトを作る

```shell
$ rails new rails6_0_0rc1
$ cd rails6_0_0rc1
```

# User の CRUD を作る

User の CRUD を作ります。

```shell
$ rails g scaffold User name
```

# UserMailer を作る

メールを送信できるように、UserMailer を作ります。

```shell
$ rails g mailer UserMailer
```

# View を変更する

`_form.html.erb` に `perform_deliveries` を true にするか false にするか指定するチェックボックスを追加します。

```erb:app/views/users/_form.html.erb
<%= form_with(model: user, local: true) do |form| %>
  ...
  <div class="field">
    <%= label_tag :perform_deliveries %>
    <%= check_box_tag :perform_deliveries %>
  </div>
  ...
<% end %>
```

# User controller を変更する

`UsersController#create` メソッドを変更し、DBへの保存が成功したら、 メールを送信するようにします。
このとき、`perform_deliveries` の値をパラメータとして `UserMailer` に渡すようにします。

```ruby:app/controllers/users_controller.rb
class UsersController < ApplicationController
  ...
  def create
    @user = User.new(user_params)

    respond_to do |format|
      if @user.save
        perform_deliveries = params[:perform_deliveries].to_i == 1
        UserMailer.with(user: @user, perform_deliveries: perform_deliveries).created_email.deliver_later
        format.html { redirect_to @user, notice: 'User was successfully created.' }
        format.json { render :show, status: :created, location: @user }
      else
        format.html { render :new }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end
  ...
end
```

以下の2行が追加した部分になります。

```ruby
        perform_deliveries = params[:perform_deliveries].to_i == 1
        UserMailer.with(user: @user, perform_deliveries: perform_deliveries).created_email.deliver_later
```

このあとは、 `UserMailer` 側を修正していきます。

# UserMailer クラスにメソッドを追加する

メールを送信する `created_email` メソッド と `perform_deliveries` を設定するコールバックを追加します。

```ruby:app/mailers/user_mailer.rb
class UserMailer < ApplicationMailer
  after_action :set_perform_deliveries

  def created_email
    @user = params[:user]
    mail(to: 'admin@example.com', subject: 'user created')
  end

  private

  def set_perform_deliveries
    mail.perform_deliveries = params[:perform_deliveries]
  end
end
```

# メールのテンプレートを作成する

今回は手抜きで テキストメールのみとし、 HTML メールは作成しません。

```erb:app/views/user_mailer/created_email.text.erb
User created
=============

User <%= @user.name %> was created
```

# メールをファイルに保存する

今回は実際にメールを送信せず、ファイルに保存するように設定を変更します。

```ruby:config/environments/development.rb
Rails.application.configure do
  ...
  config.action_mailer.delivery_method = :file
  ...
end
```

# db:migrate を実行する

```shell
$ bin/rails db:create db:migrate
```

# rails server を実行してブラウザからUserを登録する

User を登録するときに、 `perform_deliveries` チェックボックスをチェックします。
ユーザーが無事、登録できれば、メールは、tmp/mails ディレクトリ内に保存されます。
このときログを確認すると

```log:log/development.log
... Delivered mail 5ce9f3619b8f4_2f32ae97743a3b0258ac@8baf8e28fbad.mail (6.1ms)
```

とメールが送信されたことが記録されています。

今度は、`perform_deliveries` チェックボックスをチェックしないで User を登録します。
tmp/mails ディレクトリの admin@example.com ファイルには何も追加されません。
このときログを確認すると `Delivered` とはならずに

```log:log/development.log
... Skipped delivery of mail 5ce9f380b1ad6_2f32ae97743a3b025957@8baf8e28fbad.mail as `perform_deliveries` is false
```

とメールの送信をスキップしたことがログに記録されます。 (←これが今回のちょい足し機能です。)

# 試したソース

試したソースは以下にあります。
https://github.com/suketa/rails6_0_0rc1/tree/try027_perform_deliveries

# 参考情報

* [What is new in Rails 6.0](https://bogdanvlviv.com/posts/ruby/rails/what-is-new-in-rails-6_0.html)
* [Skip delivery notification when perform_deliveries is false.](https://github.com/rails/rails/pull/33824)
* [Don’t log recipients when sending mail](https://github.com/rails/rails/pull/34747)
* [Action Mailer Basics](https://edgeguides.rubyonrails.org/action_mailer_basics.html)