【Ruby on Rails】ActiveStrageでs3の画像も合わせて削除

# 目標
ローカルだけでなく、s3上の画像も削除する

# 前提
ActiveStrageでの画像削除について
詰まってしまったので、備忘録として残します。
(手探りでの記述のため、無駄があるかと思いますがご了承ください）

また、s3上の不必要な画像を削除していきます。

postモデル
カラム　name:string
アクション　new index show edit update create destroy
ルート　resouses :posts

# 開発環境
ruby 2.5.7
Rails 5.2.4.3
OS: macOS Catalina
heroku

# ActiveStrageでの画像投稿導入
[【Rails 5.2】 Active Storageの使い方](https://qiita.com/hmmrjn/items/7cc5e5348755c517458a)
こちらが大変わかりやすく、導入もしやすかったので、
こちらを参考に導入してください。

[【Rails】Herokuで画像を投稿できるようにする方法（ActiveStorage + Amazon S3）](https://qiita.com/hmmrjn/items/479c9e9ce82771f1b6d7)
herokuにデプロイする際はこちらも参考にしてください。

# ActiveStrageでの画像削除について
ここらが本題です。
[Railsガイド](https://railsguides.jp/active_storage_overview.html)には削除方法が記載されており、purgeメソッドを使用します。
このpurgeメソッドを使うことにより、railsアプリから直接s3の画像の削除が可能です。

```rb:
# avatarと実際のリソースファイルを同期的に破棄します。
user.avatar.purge

# Active Jobを介して、関連付けられているモデルと実際のリソースファイルを非同期で破棄します。
user.avatar.purge_later
```

# 1枚の画像削除について
s3の画像を削除しないといけないケースは
①画像を更新する場合②画像が紐づく投稿が削除された場合が考えられます。
また理由はまだ解明出来ていませんが、purge_laterではなく、purgeを使用すると
destroyアクションのdestroyがされず、if文がfalseとなったため、
purge_laterを使用するとうまくいきました。
1枚の画像削除についてはpurge_laterで非同期の破棄が必要かもしれません。

@post.image.purge_later && @post.destroyの順番は
先に@post.destroyをしてしまうと@post.image.purge_laterが
awsのサーバートラブル時に影響が出るかと思い、このような順番にしています。

```rb:app/controllers/posts_controller.rb
  def update
    @post = Post.find(params[:id])
    if @post.image.attached?
      @post.image.purge_later
    end
    if @post.update(post_params)
      redirect_to post_path(@post)
    else
      render :edit
    end
  end

  def destroy
    @post = Post.find(params[:id])
    if @post.image.attached?
      if @post.image.purge_later && @post.destroy
        redirect_to posts_path
      else
        render :edit
      end
    else
      if @post.destroy
        redirect_to posts_path
      else
        render :edit
      end
    end
  end

  private
  def post_params
    params.require(:post).permit(:name, :image)
  end
```

# 複数画像削除について
[Active Storageで複数画像の投稿・削除](https://qiita.com/hiroki_404_/items/e71991ac88421e5caeec)
こちらの記事を参考に実装してみましたが、
私の開発環境では、
```ActiveSupport::MessageVerifier::InvalidSignature```
というエラーになり解決出来なかったため、
画像の削除についてはすべて削除することとしました。
そのためimg_destroyアクションを新たに追加しました。

また、1枚の画像削除とは逆に複数画像削除はpurge_laterを使用すると、
たまにエラー（詳しくは覚えていませんが、処理が多すぎる？みたいなエラー）が
出ることがあるものの、purgeを使用するとうまくいきました。

@post.image.purge && @post.destroyの順番は
先に@post.destroyをしてしまうと@post.image.purgeが
awsのサーバートラブル時に影響が出るかと思い、このような順番にしています。

```rb:
  def update
    @post = Post.find(params[:id])
    if @post.update(post_params)
      redirect_to post_path(@post)
    else
      render :edit
    end
  end

  def destroy
    @post = Post.find(params[:id])
    if @post.images.attached?
      if @post.images.purge && @post.destroy
        redirect_to posts_path
      else
        render :edit
      end
    else
      if @post.destroy
        redirect_to posts_path
      else
        render :edit
      end
    end
  end

  def img_destroy
    @post = Post.find(params[:id])
    if @post.images.purge
      redirect_to edit_post_path(@post)
    else
      render :edit
    end
  end

  private
  def post_params
    params.require(:post).permit(:name,images: [])
  end
```
```rb:config/routes.rb
delete 'post_image_delete/:id', to: 'posts#img_destroy', as: 'post_img_destroy'
```
```erb:app/views/posts/edit.html.erb
<% if @post.images.attached? %>
  <%= link_to "全削除", post_img_destroy_path(@post), method: :delete, "data-confirm" => "削除してもよろしいでしょうか？", class: "btn btn-danger" %>
<% end %>
```

# まとめ
ActiveStrageでs3の画像も合わせて削除する方法でしたが、
手探りのため、より良い記述方法があるかもしれません。

私自身は、gem refile-s3のエラーが解決できなかったため、ActiveStorageに移行したため
同じ状況の人の少しでも力になれたら幸いです。詳しくは[こちら](https://twitter.com/japwork/status/1313824388903436289)

またtwitterではQiitaにはアップしていない技術や考え方もアップしていますので、
よければフォローして頂けると嬉しいです。
詳しくはこちら　https://twitter.com/japwork