Rails6 のちょい足しな新機能を試す15（Active Storage編）

# はじめに

Rails 6 に追加されそうな新機能を試す第15段。 今回のちょい足し機能は、 `Active Storage` 編です。
というか今回は Active Storage のバグフィックスじゃないかと思ってます。

Rails 5.2 では、model で validation のエラーが起ってもファイルがアップロードされていました。
Rails 6.0 では、model がDBに保存されるとファイルがアップロードされるようになっています。
実際に試した方がわかりやすいです。

記載時点では、Rails は 6.0.0.rc1 と 5.2.3 で確認しました。Rails 6.0.0.rc1 は `gem install rails --prerelease` でインストールできます。

基本的な試し方は、5.2.3 でも 6.0.0rc1 でも同じです。

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

# Rails プロジェクトを作る

```shell
$ rails new sandbox6_0_0rc1
$ cd sandbox6_0_0rc1
```

# scaffold で User を作る

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

# Active Storage を使う準備をする

```shell
$ bin/rails active_storage:install
```

# User モデルを編集する

User モデルとアップロードファイル(今回は画像ファイル)を紐づけるため `has_one_attached` を使います。
また、name に validation を追加します。

```ruby:app/models/user.rb
class User < ApplicationRecord
  has_one_attached :avatar
  validates :name, presence: true
end
```

# view を編集する

ファイルをアップロードできるように `_form.html.erb` を編集します。

avatar 用の `form.file_field` を追加します。

```erb:app/views/users/_form.html.erb
<%= form_with(model: user, local: true) do |form| %>
  ...
    <%= form.text_field :name %>
  </div>

  <div class="field">
    <%= form.label :avatar %>
    <%= form.file_field :avatar %>
  </div>

  <div class="actions">
  ...
<% end %>
```

アップロードされた画像ファイルを表示できるように `show.html.erb` に `image_tag` を追加します。

```erb:app/views/users/show.html.erb
...
  <%= @user.name %>
</p>

<p>
  <strong>Avatar:</strong>
  <% if @user.avatar.attached? %>
    <%= image_tag @user.avatar %>
  <% end %>
</p>
...
```

# controller を編集する

ブラウザからパラメータとして渡されてくる avatar をUserモデルに渡せるように `:avatar` を追加します。

```ruby:app/controllers/users_controller.rb
  private:
    ...
    # Never trust parameters from the scary internet, only allow the white list through.
    def user_params
      params.require(:user).permit(:name, :avatar)
    end
```

# DBのmigration を実行する

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

# `rails s` を実行する

```shell
$ bin/rails s
```

# 動作確認する

ブラウザで http://localhost:3000/users/new にアクセスします。

アップロードする画像ファイルを選択します。
validation error となるように name は何も入力しません。

`Create User` ボタンを押してみます。

> Name can't be blank'

のエラーになります。
ここまでは、Rails 5.2.3 でも Rails 6.0.0rc1 でも同じです。
ここで、別のコンソールで、 `storage` ディレクトリを確認してみましょう。

Rails 5.2.3 では、サブディレクトリができてファイルもできています。

```shell
$ ls -lR storage
storage:
total 4
drwxr-xr-x 3 suke suke 4096  5月 11 15:36 49

storage/49:
total 4
drwxr-xr-x 2 suke suke 4096  5月 11 15:36 gT

storage/49/gT:
total 12
-rw-r--r-- 1 suke suke 9502  5月 11 15:36 49gTM2NXg8UsKPVqTSuMNXCz
```

Rails 6.0.0rc1 では、サブディレクトリはありません。

```shell
$ ls -lR storage
storage:
total 0
```

Rails 5.2.3 では、登録画面で、ファイルを指定して、validation エラーを起こす度に、 `storage` ディレクトリの中にファイルが増えていきます。
Rails 6.0.0rc1 では、DBに保存できた時に初めて、 `storage` ディレクトリの中にファイルができます。

# ソース

今回のソースは以下から参照できます。
https://github.com/suketa/rails6_0_0rc1/tree/try015_active_storage

# 参考情報

* [What is new in Rails 6.0](https://bogdanvlviv.com/posts/ruby/rails/what-is-new-in-rails-6_0.html)
* [Store newly-uploaded files on save rather than assignment](https://github.com/rails/rails/pull/33303)
* [現場で使える Ruby on Rails 5速習実践ガイド](https://www.amazon.co.jp/%E7%8F%BE%E5%A0%B4%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B-Ruby-Rails-5%E9%80%9F%E7%BF%92%E5%AE%9F%E8%B7%B5%E3%82%AC%E3%82%A4%E3%83%89/dp/4839962227/) (Active Storage を使う際、参考にさせていただきました。）