坂ノ途中の技術日誌

環境負荷の小さい農業を広げるためのあれこれに取り組む株式会社坂ノ途中の技術ブログです。

脱 Paperclip2023. ActiveStorage への移行アイデアいくつか

はい。2023年の記事です。多くの方々はとっくの昔に対処済みで、2023年にもなっていまさらという記事かと思いますが、さまざまな事情でいまだ需要がある気配を感じています。そこで、恥ずかしながら取り組んだことを共有します*1

まとめ

  • Ruby on Rails のファイルアップロードとその表示を便利にする Gem である Paperclip は2018年にメンテナンス停止し非推奨になりました
  • 移行先は ActiveStorage が順当
  • 公式のドキュメントでは migration で移行する方法が案内されているけれど元のファイルパスを変えずに登録する手順のため少しわかりにくい
  • ファイル数が少ない見込みであれば全部移行してもいいかもしれない
  • PaperClip と ActiveStorage 同時に(別名で)ファイルをもつようにすると非同期でできて便利

前提:PaperClip について

2007年から提供されている、画像やファイルアップロードを扱う有名 Gem でお世話になった方も多いと思います。

github.com

Rails5.2で ActiveStorage が登場したこともあって2018年に deprecated になりました。移行しましょう。 とはいえ、小規模なあまり手がかけられていないアプリケーションではそうなっていない場合もあると思います。

事前準備:ActiveStorage の動作と移行の検証

公式の手順では S3 上のファイルを動かさずに ActiveStorage が把握するファイルの先を更新するためにわかりにくくなっています。ActiveStorage の動作について簡単なコードでファイルを移動させる例をあげてみます。

たとえば、Attachment クラスにて以下のように Paperclip を利用しているとします。

has_attached_file :document

このとき以下のように ActiveStorage をもつよう移行したい状況を考えます。

has_one_attached :active_document

以下のように attach メソッドで paperclip で管理していたリソースをActiveStorageに移せます(※ただし、このやり方だとクラウドストレージは倍かかるし実ファイルを移行することになり時間もかかるので ActiveStorage の理解のための動作確認とお考え下さい)。 こうして試してみてから active_storage_attachments と active_storage_blobs にできたレコードをみると、公式で案内する手順を理解しやすくなると思います。

document_url = attachment.document&.url(:original)

uri = open(document_url)

attachment.active_document.attach(
  io: open(document_url),
  filename: attachment.document_file_name,
  content_type: attachment.document_content_type
)

ファイル数が少ないなどでしたらこの方式で公式の migration スクリプトを書き替えて実行してもよいかもしれません。

作戦

以下のように、Paperclip と ActiveStorage を両方有効にし、追加・更新時に両方に保存するようにすると移行のダウンタイムを小さくできます。そして表示時には、 attached? メソッドで ActiveStorage にリソースがある場合はそちらを表示するようにする。

has_attached_file :document
has_one_attached :active_document

あとはテストをしながら愚直に書き換えていくだけです。画像利用クラスや箇所が多いとたいへんです。いくつかはまりどころがあったので触れておきます。

ActiveStorage でのはまりどころ

1.画像を変更できるかどうか

これまでは、事前にクラスにサイズを指定しておくと変換したものを用意してくれて image.url(:thumb) などで簡単に表示できましたが、そうすることはできませんし、ファイルによって変更できるかどうかで分岐させる必要があります。

たとえば画像を

has_one_attached :active_image

として保存している場合、以下のように variable かどうかをみる必要がある。

  def image_icon_size
    if active_image&.attached? && active_image&.variable?
      active_image.variant(resize_to_fill: [100, 100])
    else
      active_image
    end
  end

2.公開URLは Rails6.1 までない

ActiveStorageではデフォルトではファイルへのURLはrailsを経由し、有効期間が短い。 このままではCDNがキャッシュしにくいので公開して問題ない場合はURLを公開するとよい。

ただ、この設定はRails6.1からなので移行時にバージョンを確認しておいてください。

Rails 6.1 つまみ食い② : ActiveStorage の永続的なURL - インゲージ開発者ブログ

3.PDFで画像を使う場合、テストで気付きにくい

wicked-pdf でPDFを生成し、そこでActiveStorageの画像を用いる場合、background-image で設定すると表示されないようです(謎)。 素直に image タグを使って解決させました。 PDFでの表示内容、自動テストで確認しにくく見逃してしまっていました・・・。

4.ファイルのバリデーション

公式ドキュメントで探しにくかったのですが、下記の記事が参考になりました。感謝

ActiveStorageのバリデーション - Qiita

まとめ

そんなこんなで 脱PaperclipしてRailsのバージョンも7まであげてすっきりしました。 振り返ってみると、Paperclipはファイルアップロードをシンプルに使えてたいへん便利だったことがわかります。ほんとありがとうございました。

生産者さまがデータを活かした営農をできるようにするファーモを開発している片山がお届けいたしました。ご質問やコメントあればお気軽にお知らせください。

ファーモ | 農家のための無料で使える販売管理

参考URL

公式の脱出ガイド https://github.com/thoughtbot/paperclip/blob/master/MIGRATING.md

日本語訳(感謝!)
Rails: PaperclipからActiveStorageへの移行ガイド by thoughtbot(翻訳)|TechRacho by BPS株式会社

新登録されたファイルをどちらにも登録することでダウンタイムゼロを目指す戦略
From Paperclip to Active Storage: An incremental, zero-downtime approach | TokyoDev

ActiveStorage の仕組みとコードリーディングについてはこちらの記事がたいへん参考になりました感謝。
[Rails5.2]ActiveStorageの仕組み(図あり)と使ってみてわかったこと - Qiita

Active Storage Overview — Ruby on Rails Guides