blog.tawa.me

東京から福岡移住したWeb開発の人のブログ。適当に何でも置きます。

Ruby on RailsのActive StorageをAPI モードで使うなら絶対検討すべきこと

f:id:tawachan39:20181209172556j:plain

Ruby on Rails の 5.2 から標準で入った Active Storage。

Rails 5.2 が公式リリースされる前から本番登用していたので、 使い始めてからそろそろ使用してから1年が経つ。

現在仕事で関わっているサービスでは Rails は APIサーバーとして運用しているが、 Acitve Storage をファイルを中央集権的に管理するように使ったことでとても幸せになれているのでその知見を書いておきたい。

APIサーバーとして Rails を使っていて、Active Storage の導入を考えている方はぜひ一度この使い方を検討してみてほしい、

※ この使い方に関しては、Active Storage に限らず carrierwave とかでも活かせるかもしれない。

Active Storage とは

Active Storage とはどんなものかは公式のドキュメントを見るほうが早いと思う。

Active Storage Overview — Ruby on Rails Guides

中央集権管理とは

これは造語的に使っているので、どんなものなのかを説明する。

マニュアル通り使うときっとこうなる

画像などのファイルを紐づけたいモデルに対して、Active Storage によるアップロード機構を付けていくことになるはずである。 そうなると、違うモデルにまたファイルを紐づけたいときには、同じことをそのモデルに対しても行うことになる。

f:id:tawachan39:20180930141856p:plain

一方で中央集権管理的とは

中央集権管理的とは、このように Active Storage と紐づくだけのモデルを1つ作り、他のモデルからこのモデルを参照するイメージ。

f:id:tawachan39:20180930142031p:plain

こうすることで、追加的にファイルを使いたいモデルができたとしても、Acitve Storage のファイルを管理しているモデルへの参照をつけるだけ。

これは、いつも通りの Rails の外部参照なのでなんの問題もなくできるのがよいところ。

中央集権管理のメリット

まず、Active Storage 特有の処理が1つのモデルに集約されるので見通しがだいぶ良い。

さらに、ユーザーがアップするファイルがここに集まっているので、監視がしやすい。

規約違反のものなどをチェックする必要のあるサービスには大きなメリットかもしれない。

また、同じファイルを複数のモデル・レコードで使い回せるのはよい。参照先の id を変えればすぐに参照を切り替えられる手軽さは大きい。 (中央集権管理していないと同様のファイルをアップロードし直さないとファイルを張り替えられない)

データの更新とファイルアップロードのタイミングがわかりやすい

もう1つメリットとしては、データの更新とファイルのアップロードのタイミングがわかりやすいという点。

マニュアル通りの場合

以下のような更新の仕方だと、通常の update と画像の変更のタイミングが違ってくる。

class Api::UsersController
  def update
    image_file = params[:image]
    @user = User.find(params[:id])
    @user.update!(user_params)
  
    # ↓ ここで例外発生した場合どうする?
    @user.image.attach(image_file)
  end
end

Rollback をするのか、画像だけアップロードに失敗したということにするのか決めれば問題ないのだが、 全てのモデルで同じ挙動をするかは実装者によってしまうので意図せぬバグを埋めてしまいそう。

中央集権管理の場合

画像をアップロードする処理とその他の update を分けられるのでよい。

class Api::ImagesController
  def create
    image_file = params[:image]
    @image = Image
  .create!(images_params)
    @image.image.attach(image_file)
  end
end

上の変更が成功してから id を入れて普通の update 処理をすれば見通しが良くなる。

class Api::UsersController
  def update
    image_id = params[:image_id]
    @user = User.find(params[:id])
    @user.update!(image_id: image_id)
  end
end

まとめ

APIサーバーとして Rails を使っている場合、中央集権管理にしたほうがだいぶ見通しが良くなると思っている。

Rails で html まで返す従来のアプリケーションであれば、 それぞれのモデルに Active Storage を紐づけたほうが一回のリクエストで更新処理が行う都合上いい気がする。

しかし、APIサーバーとして運用しているのであれば、その限りではないはず。 あまり見ない使い方ではあるが、ぜひ一度検討してみると良いかもしれない。