Rails5.2から入る新機能ActiveStorageを使うべきか?

先月 [Rails5.2.0 beta](http://weblog.rubyonrails.org/2017/11/27/Rails-5-2-Active-Storage-Redis-Cache-Store-HTTP2-Early-Hints-Credentials/) がリリースされたこともあり、ActiveStorage推し・使ってみた関連のエントリーをよく見るようになりました。しかし、ネット上で見かけるActiveStorageへの温度感と、[Ginza.rb 第51回](https://ginzarb.doorkeeper.jp/events/64670) に参加してActiveStorageの実装を読んだエンジニアの皆さんの温度感に差を感じています。
Rails5.2.0リリース後、ファイルアップローダとしてActiveStorageを採用していいのでしょうか？

# ActiveStorageとは何か

Rails5.2.0からrails/railsに入るファイルアップローダです。
複数のクラウドストレージ、ミラーアップロード、S3へのダイレクトアップロードなどの機能を備えています。
carrierwave、refile、shrineなどのgemが存在する中、なんでファイルアップローダを実装したのかちょっと不思議ですね。

その他の機能詳細は [ActiveStorageのREADME.md](https://github.com/rails/rails/blob/master/activestorage/README.md) を参照。

# ActiveStorageの問題点

基本的な部分に問題はなさそうですが、[本エントリーを書いている時点](https://github.com/rails/rails/tree/d9e4bffb60f69d972311fa7d23f4ce540adc54a9)で以下の機能が実装されていません。

1. バリデーション
1. キャッシュ

ほぼ全てのファイルアップローダ関連のライブラリでサポートされているこれらの機能が無い事がActiveStorageを採用する場合に確認するべき点です。

## 1. バリデーションが無い問題

バリデーションが無いと当然、ファイルのバリデーションが出来ません。

自分は前職で主に広告配信サービスの管理画面の開発を担当していたのですが、広告のバナーの縦幅横幅の長さやファイル自体のサイズ、content-typeなどのバリデーションが必須でした。[広告サイズには様々な種類があり](https://support.google.com/adsense/answer/6002621?hl=ja)、それぞれのフォーマットに合わせたクリエイティブを指定する必要があります。ところがバリデーションが出来ないと、レクタングル(中)で入稿したいと思っているユーザーが間違えてレクタングル(大)で入稿してしまった場合でも正常終了してしまいます。
このように、バリデーションをしないとアップロードするファイルサイズやフォーマットなどに制限をかけたい場合に問題になります。

## 2. キャッシュが無い問題

ファイルのアップロードフォームとその他の項目のフォームが存在した場合、以下のような問題が発生します。

### (1) 前提条件

プロフィール画像と名前を登録できるプロフィール編集画面がある場合。

![1.jpg](https://qiita-image-store.s3.amazonaws.com/0/3311/9fa25974-e52f-faca-b5dc-3aa47268c460.jpeg)

### (2) フォーム内容の入力とプロフィール画像のアップロード

1. 表示名を入力(31文字)
1. プロフィール画像をアップロード
1. 更新ボタンをクリック

![2.jpg](https://qiita-image-store.s3.amazonaws.com/0/3311/28165346-6aaa-4570-81f8-9fdfbb5cb029.jpeg)

### (3) 実行結果

1. 表示名欄のバリデーションエラー
1. 表示名欄に「名前」と入力
1. 更新ボタンをクリック

![3.jpg](https://qiita-image-store.s3.amazonaws.com/0/3311/296c8742-d129-ab04-888f-f1bf3c2f219e.jpeg)

### (4) 正常終了

表示名は正常値なので更新できていますが、キャッシュが無い為アップロードしたプロフィール画像は消えてしまいます。

![4.jpg](https://qiita-image-store.s3.amazonaws.com/0/3311/7c92421a-756f-eda5-0732-04c2a1da112c.jpeg)

これではプロフィール画像に問題が無くても、その他のフィールドでバリデーションエラーになった場合、その都度ファイルをアップロードし直さなければなりません。
これが「仕様です」で通ればいいのですが、そうもいかない事が多いのがこの世界なのは御存知の通り。

## 対策

バリデーションもキャッシュもサポートされていないのであれば自前で実装するという方法がありますが、工数と保守のコストを考えると既存のファイルアップローダライブラリを採用する方が低コストなので現実的だと思われます。

## 今後のアップデートでバリデーション機能が実装される可能性

キャッシュはまぁ、いいとして問題はバリデーション機能が無い事です。
これがなければ少なくとも仕事で使う気にならない人が大半なはず。
まず、ActiveStorageは[ポリモーフィック関連](http://guides.rubyonrails.org/association_basics.html#polymorphic-associations)を使ってファイルのデータをBlob,Attachmentモデルに集約させる作りになっています。
つまり、バリデーションを実装するとなると全てのバリデーションをBlob,Attachmentに集約させて定義する・・・？のかどうか。ちょっと今のActiveStorageの実装を見る限りだと、どうやって実装すればいいのか非常に難しい状況になっているので、あまり期待はできなさそう。
もしサポートされたら実装を読んでみたいですね。

# まとめ

というわけで、当たり前といえば当たり前の内容ですが、ActiveStorageがrails/railsに取り込まれたからと言って安心して使える状況ではないよという話でした。おそらく[Basecamp](https://basecamp.com/)ではActiveStorageの機能で問題が無いだけであり、この記事を読んでいるあなたが必要としている機能が完備されているとは限らないのです。

# [補足]ActiveStorageが駄目だった場合は何を選べばいいのか

結論から言えば[willnetさんのスライド(RejectKaigi2017)の冒頭](https://speakerdeck.com/willnet/file-upload-2017)と同じく「要件による」で終わってしまうんですが、あえて1つ選ぶとすれば [Shrine](https://github.com/janko-m/shrine) です。
Shrineは多機能なライブラリですが、主に気に入っている部分は以下の点で、

- Core機能をシンプルにし、各機能をプラグイン形式で選べる
- 多くのストレージに対応している
- 各種ストレージへのダイレクトアップロードをサポート
- 開発が活発
- [ドキュメント](http://shrinerb.com/)の充実(CarrierWave,Paperclip,Refileなど他のファイルアップローダからの移行ガイドもある)

その中でもプラグイン形式で機能を選べる柔軟性の高さに惹かれました。
自分はRailsが好きなので、ある程度レールが引かれているライブラリが好きではあるのですが、ファイルアップローダに関しては柔軟にカスタマイズできるライブラリが適していると考えます。アップロードされるファイルの種類・保存場所・UI...など状況によって実装方法が全く変わってしまうし、考慮するべき点が非常に多くなってしまうからです。

# 追記

https://twitter.com/georgeclaghorn/status/1010197761684377601
Rails6でバリデーションが入る・・っぽい。