PaperclipからActiveStorageに移行する（AWS）

PaperclipでAWS上にデータを保管していましたが、Rails5.2から追加されたActiveStorageの利用を推奨として、Paperclipがdeprecatedになったため、ActiveStorageへの移行を行うことにしました。

Paperclip内の移行手順のドキュメントに従うことで基本的にはスムーズに移行できますが、英語が完璧でなかったり細かいイレギュラーな部分でつまずいたので、備忘録として残しておきます。

- PaperclipのActiveStorageへの移行ドキュメント https://github.com/thoughtbot/paperclip/blob/master/MIGRATING.md
- 上記の日本語翻訳版 https://techracho.bpsinc.jp/hachi8833/2018_05_21/56668
※一部原文が追加更新されていてこちらにない部分があります

**作業順**

1. ActiveStorageのインストールとストレージの設定
1. mini_magickのgem追加
1. paperclipのgemを外す
1. モデルにActiveStorageの設定をする
1. 元データをActiveStorageのレコードに再登録する ★
1. Viewの更新
1. default_url対応 ★

重点はこの★のところだけです。あとは、ドキュメントどおりなので割愛。

# ActiveStorageにデータを移行する
上記作業順の1,2,3まで終わったら、モデルにActiveStorageの設定をします。

```rails:User.rb
	has_one_attached :photo
```

## 元データからActiveStorageのレコードに再登録する
rakeタスクで、元データを一つずつ順番にActiveStorageデータとして再登録します。
この作業の良い点

- development環境からできる。
- 元データを一旦残した状態にしておける。（最悪問題があった場合に救出できる）

一つのデータあたりにかかる時間は画像サイズにもよりますが、平均1,2秒程度でした。移行したいもののデータ量によっては、実行するタイミングを考えた方が良さそうです。

paperclipの移行ドキュメントに掲載されているrakeタスクからの変更点は以下のとおりです。

- ファイル名に日本語URLが含まれている場合の対応
- ファイルがなぜかなくなっていてエラーが出たので、ファイルが開けない場合の対応

※また私の場合、photoの親モデルであるUserモデルのコールバックが悪さをしてつまずきました。このタスクは一回しか走らせないので、いらないコールバックはコメントアウトしておくと安全かも。

```ruby:lib/tasks/users.rake
namespace :users do
  desc 'Migrate paperclip to ActiveStorage'
  task migrate_to_active_storage: :environment do
    User.where.not(photo_file_name: nil).find_each do |user|
	   # 移行前のオリジナルファイル名を参照
      image = user.photo_file_name
      # 以下の一行はpaperclipの移行ドキュメントにはありましたが、URLパターンによっては不要
      # ext = File.extname(image) 
      
      # 移行前のURLパターン
      image_original = CGI.unescape("#{user.id}_#{image}")

      # 移行前URLのフルパス
      # URI.encodeで日本語や記号を含むファイル名に対応
	  photo_url = URI.encode "https://s3.amazonaws.com/hogehoge/photos/original/#{image_original}"
	  
	  # open()で何らかの理由でファイルが開けなかった場合にエラーを返す
      begin
        uri = open(photo_url)
      rescue => e
        p "ERROR: #{e}"
      end
      p "URL #{photo_url} e? #{e.blank?}"
	
	  # ファイルが正しく開けたら、ActiveStorageで保存
      user.photo.attach(io: open(uri),
        filename: user.photo_file_name,
        content_type: user.photo_content_type) if e.blank?
    end
  end
end
```

上記のrakeタスクを実行。

```
$ rake users:migrate_to_active_storage
```

## mini_magickの追加
ドキュメント内にそっと書かれていた、`mini_magick`を追加してねという一文をスルーしてしまったために、この流れの最後までやって試したときに、以下のようなエラーが出ました。

```
LoadError (cannot load such file -- mini_magick)
```

どのタイミングでも良いので、mini_magickを入れましょう。

```Gemfile
gem 'mini_magick'
```

## paperclipのdefault_url・・・
paperclipのdefault_url設定の良い移行の仕方を探していたのですが、ActiveStorageではその機能はない模様。
decoratorやhelperメソッドでカバーするしかなさそうです。

私の場合は、Userのアバターがない場合の表示をするのに、decoratorで対応しました。(gem active_decorator）

```ruby:user_decorator.rb
module User
  def avatar(size='mini')
    px =
      case size
        when 'mini' then MINI
        when 'thumb' then THUMB
        when 'small' then SMALL
        else MINI
      end
      # MINI,THUMB,SMALLの定数はconfig/initializers/constants.rbにまとめて持っている

    if self.photo.present?
      image_tag(self.photo.variant(resize: "#{px}x#{px}"), class: 'avatar')
    else
      # webpackerで画像を管理。cssで画像サイズを調整。
      image_pack_tag("noimage.png", class: "avatar #{size}")
    end
  end
end
```