技術共有/Active Storageを導入してみたら結構大変でした。

はじめに

こんにちは! ココナラ法律相談を担当しているたにやんです。
ココナラ法律相談(https://legal.coconala.com) の画像管理用のgemをpaper clipから Active Storageに変更したのでそれをまとめようと思います。

経緯

先月、ココナラ法律相談のディレクターから「弁護士が自分で顔写真を変更できる機能作って欲しいんだけど」
自分「了解です」
自分「paper clip経由で保存したらええやろー とりあえずgemのドキュメント確認するかーーー」

f:id:coconalainc:20181023165502p:plain

????? f:id:coconalainc:20181023165259p:plain

まじかーーー

せっかくだしココナラ法律相談のRailsを5.2にしたばかりなのでgemを他のいい感じのにしますかー
https://robots.thoughtbot.com/closing-the-trombone
↑泣けます 実は10年以上前から開発がスタートしていたgemだったんですね。
お疲れ様でした。&ありがとうございました。

ってことでCarrierWaveにしようかなと思ったのですが、
またgemの保守が終わったら大変なので
今回Rails謹製のgem Active Storage (https://edgeguides.rubyonrails.org/active_storage_overview.html) を使用する事を決めました。
後、paper clipのドキュメントにマイグレーションガイド(https://github.com/thoughtbot/paperclip/blob/master/MIGRATING.md )が書かれていたのでそれを元に作業していきます。

下準備

Gemfileにgem 'activestorage'を追記して

require "rails"
〜
〜
〜
require "active_storage/engine"
bundle exec rails active_storage:install

Copied migration YYYYMMDDHHMMSS_create_active_storage_tables.active_storage.rb from active_storage

↑のメッセージが表示されたらokです。

bin/rails db:migrate

↑のコマンドを実行してあげると以下の2つテーブルが作成されます。
active_storage_blobs / active_storage_attachments

このテーブルにファイルのデータが保持されていきます。
paper clipでは該当テーブルに対してその画像データが保持される用のカラムがありましたが、
Active Storageでは全く別テーブルに保持されるようになります。

このおかげで関心の分離を行えていいかなと思います。1レコードに対して複数の画像を持つ場合もこれで実現が簡単になるかなと思います。

ただ、他の人が初めて Active Storageが使用されているアプリケーションを触る時にどこに画像のレコード及びデータがあるのか探すの大変そうなので、チーム内での共有はしないといけません。(当たり前ですけど)

テーブル

active storage attachment テーブル

record_typeにmodel名が
record_id にmodelのidが
blob_idはactive_storage_blobというもう一つのテーブルのidが入ります。

スクリーンショット 2018-10-22 15.53.51.png (42.6 kB)

active storage blob テーブル
ファイル名などが記述されます。(割愛)

aws設定

config/storage.ymlファイルが生成されているので

# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)

以下に設定を反映します。
今回はawsを使用しているので

config/environments 以下の環境ファイルに

config.active_storage.service = :amazon

を追記します。

model部分に追記

そして、各modelにアソシエーションを書くように以下のコードを追記するとActive Storageを使用する事ができます。

1レコードに対して1つ画像を持つ場合

has_one_attached :image

1レコードに対して複数持つ場合

has_many_attached :images

既存データ移行する

以下のブログを参考にして作業しました。
https://techracho.bpsinc.jp/hachi8833/2018_05_21/56668

ココナラ法律相談はレコードに対して必ず画像があるわけではないので条件分岐を書く必要があって
画像を使えば使うほどここが大変になります。

viewの修正

public以下に置く場合とcloud(今回はs3)に置く場合は及び配信方法によって使用するメソッドが異なります。
ケースバイケースなので割愛しますが、自分はs3の直リンクを表示させています。
下記の懸念事項で書いている本番環境で画像パスが404になってしまうためです。

苦労したところ

Active Storageにvalidationがない

ActiveStorage attachment validations not working(https://github.com/rails/rails/issues/31656)

Active Storage doesn’t support blob validations yet, so this is the expected
そんなものはないでござる(でも検討中だよ)

base64でencodeされた画像のdecodeができない

今(2018/10)の所ない GitHub上のissuesでは議論が行われていてこれから実装する予定と書かれていました。
そのため decodeは自分で実装しないといけないです。

何故か本番環境で画像が404になる

エラーハンドリングの設定をroutes.rbに書いていると画像パスへのurlをキャッチしちゃいます。
参照
https://qiita.com/mknstone/items/d027070ed0d004ffc993

このために自分達のアプリケーションのかみ合わせが悪くて、active stroageのviewメソッドを使用しておりません。

N+1が大量発生するんだけど

with_attached_attachment_nameというスコープを使用してeager loadしてあげましょう。
(自分も最初忘れてました)
参照 https://qiita.com/ozin/items/f4aea5b244a6aa03caee

結論/感想

最初に調べた時はmigrationガイドもあって簡単にできるかなーと思っていたのですが、
ドキュメントとかもあまりなくて結構時間がかかってしまいました。

Active Storageはシンプルな機能しかないので採用は慎重にした方がいいかなと思います。
Railsのファイル管理のgem選定って難しいですね。

後、自分の使い方が悪いかもしれないのですが、s3のクラウド上で全てのファイルがルートディレクトリに展開されてしまいます。
全てハッシュされたファイル名で展開されているのであまり整理されているという感覚にはならないので、少し違和感を感じました。
GitHubのissuesでこの議題が上がっていて現状はこれしかないという趣旨の返答があったのでこれで運用しています。
(もし、もっといいやり方をご存知の方は教えて頂けると嬉しいです。)

以上です。 ご拝読ありがとうございました。 f:id:coconalainc:20181023175934p:plain


---
お知らせメール登録
→こちらにご登録いただけましたら、よもやまブログの更新情報やイベント情報などを随時メールでお知らせします。