CarrierwaveとActiveStorageのバリデーション後の画像表示について

#概要
Railsで開発をしている際に、CarrierwaveとActiveStorageを用いて保存に失敗する（バリデーションなど）と画像データがリセットされることがあるかと思います。
この記事では、保存に失敗した後画像を保持するためにどのようなコードを記述する必要があるかについて記載させていただきます。
`※もし、コードで誤りやリファクタリングできる部分があればコメントにてご教授いただけますと幸いです`

実行環境
- ruby 2.6.5
- rails 6.0.0
- OS mojave
#Carrierwave
##使用する方法
Carrierwaveではcacheという保存に失敗した際にデータを保持してくれる機能があるのでこちらを使用して行きます。
[carrierwaveuploader/carrierwave: Classier solution ... - GitHub](https://github.com/carrierwaveuploader/carrierwave)
>検証に失敗すると、アップロードされたファイルが消えることがよくあります。CarrierWaveには、その場合でもアップロードされたファイルを簡単に思い出せる機能があります。userモデルにアップローダーがavatar ファイルにマウントされていると仮定して、非表示フィールドを追加しますavatar_cache（必要に応じてattr_accessibleリストに追加することを忘れないでください）。Railsでは、これは次のようになります。

```new.html.erb
<%= form_for @user, html: { multipart: true } do |f| %>
  <p>
    <label>My Avatar</label>
    <%= f.file_field :avatar %>
    <%= f.hidden_field :avatar_cache %>
  </p>
<% end %>
```
##コード例
`※rails newなどは実行している前提で記載させていただきます`
###1.Imagemagickのインストール
ターミナルで以下のコマンドを実行してください

```
brew install imagemagick
```

###2.gemのインストール
以下のコードをGemfileに追加してbundle installを実行してください

```Gemfile
gem 'carrirerwave'
gem 'mini_magick'
```

###3.postモデルとpostsコントローラーを作成
ターミナルで以下のコマンドを実行してください

```
rails g model post
rails g controller posts
```

###4.マイグレーションの変更
3にて生成されたマイグレーションファイルに以下のコードを追加し、dbをcreate,migrateしてください

```20200xxxxxxxxx_create_posts.rb
  t.string :image
  t.text :content
```

###5.carrierwaveの設定
ターミナルで以下のコマンドを実行してください

```
rails g uploader image
```
上記、コマンドを実行すると以下のファイルが作成されますのでコードを追記してください

- app/uploaders/image_uploader.rb

```app/uploaders/image_uploader.rb
include CarrierWave::MiniMagick
process resize_to_fit: [200, 200]
```
post.rbにも以下のコードを追加して下さい

```app/models/post.rb
mount_uploader :image, ImageUploader
```
###6.画面に表示するための準備
以下のようにルーティングの設定をしてください

```routes.rb
  root "posts#new"
  resources :posts
```
以下のようにコントローラーの設定をしてください

```app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def new
    @posts = Post.all
    @post = Post.new
  end
  
  def create
    @post = Post.new(post_params)
    @post.image.retrieve_from_cache! params[:post][:image_cache] if !@post.image.present? && params[:post][:image_cache].present? 
    if @post.save
      redirect_to root_path
    else
      @posts = Post.all
      render :new
    end
  end

  private
  def post_params
    params.require(:post).permit(:image, :content)
  end
end
```
以下のようにビューファイルを作成し記述してください

```app/views/posts/new.html
<p>保存に失敗した画像</p>
<%= image_tag @post.image.url if @post.image.present?%>

<%= form_with(model: @post, local: true) do |f| %>
  <%= f.file_field :image %>
  <%= f.hidden_field :image_cache %>
  <%= f.text_field :content%>
  <%= f.submit%>
<%end%>


<p>保存されている画像一覧</p>
<% @posts.each do |post|%>
  <%= image_tag post.image.url if post.image.present?%>
  <p><%= post.content%></p>
<%end%>
```
postモデルに以下の記述を追加しください

```app/models/post.rb
validates :content, presence: true

```
###7.挙動確認
1.rails sをしてlocalhost:3000を開くと以下のような画面が表示されているかご確認ください
<img width="649" alt="carrierwave_1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355423/e489ec60-06fc-f13f-b9d0-d8928a134a37.png">
2.ファイル選択ボタンから画像を選択してCreate Postボタンを押してください（テキストは何も入力しないでください）
3.contentのバリデーションに引っかかるのでもう一度投稿画面に遷移されます
その時、先ほど投稿した画像が以下のように画面に表示されているかご確認ください
<img width="247" alt="carrierwave_2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355423/e8e1bfb3-8be5-4e4c-76fa-26985500fcd6.png">
4.画像は選択せずにテキスト入力欄に「test」という文字を入力してCreate Postボタンを押してください
5.先ほどの画像とtestという文字が保存され、保存されている画像一覧に以下のように表示されます
<img width="540" alt="carrirerwave_3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355423/28981443-322f-2367-b9cb-4ef92df9c153.png">

#ActiveStorage
##使用する方法
ActiveStorageにはダイレクトアップロードという機能があります。
今回はその機能を使い、保存に失敗した画像を再利用する方法を紹介して行きます。
[Active Storage の概要 - Railsガイド](https://railsguides.jp/active_storage_overview.html)
>1.アプリケーションのJavaScriptバンドルにactivestorage.jsを追記します。
アセットパイプラインを使う場合は以下のようにします。

```application.js
//= require activestorage

// rails6.0の場合はデフォルトで以下の記述が入っているため特に変更していただく必要はありません
require("@rails/activestorage").start()
```
>2.ファイル入力に以下を記述してダイレクトアップロードのURLを指定します。

```
<%= form.file_field :attachments, multiple: true, direct_upload: true %>
```
>3.以上で完了です。アップロードはフォーム送信時に開始されます。

##コード例
`※rails newなどは実行している前提で記載させていただきます`
###1.postモデルとpostsコントローラーを作成
ターミナルで以下のコマンドを実行してください

```
rails g model post
rails g controller posts
```

###2.マイグレーションの変更
1にて生成されたマイグレーションファイルに以下のコードを追加し、dbをcreate,migrateしてください

```20200xxxxxxxxx_create_posts.rb
  t.text :content
```
###3.ActiveStorageの設定
ターミナルで以下のコマンドを実行してください

```
rails active_storage:install
rails db:migrate
```
Gemfileに以下のコードを追加してbundle installをしてください
`※この時にrails sをし直してください`

```Gemfile
gem 'mini_magick'
```
###4.画面に表示するための準備
以下のようにルーティングの設定をしてください

```routes.rb
  root "posts#new"
  resources :posts
```
以下のようにコントローラーの設定をしてください

```app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def new
    @posts = Post.all
    @post = Post.new
  end

  def create
    @post = Post.new(post_params)
    @post.image.attach(ActiveStorage::Blob.find(@post.image_blob_id)) if !@post.image.attached? && @post.image_blob_id
    if @post.save
      redirect_to root_path
    else
      @posts = Post.all
      render :new
    end
  end

  private
  def post_params
    params.require(:post).permit(:image, :content, :image_blob_id)
  end
end
```
以下のようにビューファイルを作成し記述してください

```app/views/posts/new.html
<p>失敗した画像</p>
<%= image_tag @post.image.variant(resize:'200x200').processed if @post.image.attached? %>
<%= form_with(model: @post, local: true) do |f|%>
  <%= f.file_field :image, direct_upload: true %>
  <%= f.hidden_field :image_blob_id, value: @post.image.blob_id if @post.image.attached?%>
  <%= f.text_field :content %>
  <%= f.submit%>
<% end%>

<p>保存されている一覧</p>
<% @posts.each do |post|%>
  <%= image_tag post.image.variant(resize:'200x200').processed if post.image.attached?%>
  <p><%= post.content%></p>
<% end%>
```
postモデルに以下の記述を追加しください

```app/models/post.rb
  attr_accessor :image_blob_id

  has_one_attached :image

  validates :content, presence: true

```

###5.挙動確認
1.rails sをしてlocalhost:3000を開くと以下のような画面が表示されているかご確認ください
<img width="575" alt="active_1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355423/46c141dc-d2b1-0ac1-bab8-4b20597e98d4.png">

2.ファイル選択ボタンから画像を選択してCreate Postボタンを押してください（テキストは何も入力しないでください）
3.contentのバリデーションに引っかかるのでもう一度投稿画面に遷移されます
その時、先ほど投稿した画像が以下のように画面に表示されているかご確認ください
<img width="247" alt="carrierwave_2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355423/e8e1bfb3-8be5-4e4c-76fa-26985500fcd6.png">
4.画像は選択せずにテキスト入力欄に「test」という文字を入力してCreate Postボタンを押してください
5.先ほどの画像とtestという文字が保存され、保存されている画像一覧に以下のように表示されます
<img width="540" alt="carrirerwave_3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355423/28981443-322f-2367-b9cb-4ef92df9c153.png">

`※以上で終了になります。
もし、誤りなどあればコメントからご連絡いただけますと幸いです`