【Rails】policiesディレクトリの使い方

目的

railsapp/policiesディレクトリを作るときに、中身には何を入れるのかまとめる。

パーフェクトRuby on Railsの9章を読んでいて、コールバック・バリデーションをモデルから分離して、独立したクラスを作る方法を学びました。

パーフェクト Ruby on Rails

パーフェクト Ruby on Rails

それらはappディレクトリ以下に、callbacksvalidatorsという他にも、app配下に置いておくものとしてpoliciesというディレクトリが有るようです。
ただし、調べてみるとpoliciesディレクトリの使い方は、そこまで一般解があるように思えなかったので、一旦まとめておいて今後検討する材料にしようかと思いました。

policiesディレクトリの使い方概要

  1. punditを使う(わりと一般的?)
  2. cancancanのavility.rbを、modelに入れずにpoliciesに入れる(avilitiesでもいいと思う)
  3. 特にgem等は使わず自分でピュアRubyのオブジェクトを定義して入れる
  4. policyっていうgemを使う(3をちょっと便利にした感じのもの。メンテされてる??)

punditを使う

app/policiesは以下のようなクラスが入ります

# app/policies/post_policy.rb
class PostPolicy
  attr_reader :user, :post

  def initialize(user, post)
    @user = user
    @post = post
  end

  def update?
    user.admin? or not post.published?
  end
end

punditの使い方に関しては以下を
Rubyist Magazine - 権限管理のgem、Punditの紹介 参考
Railsを責務に応じてリファクタリングするための9つのパターン | Simplie Post

gemを自然に使うとこうなるため、サンプルには事欠かなさそう。

cancancanのavility.rbを、modelに入れずにpoliciesに入れる

app/models/avility.rbが肥大化してイヤだ!というときにapp/policiesに入れるという選択肢があるらしい。
中規模Web開発のためのMVC分割とレイヤアーキテクチャ - Qiita
Separating abilities in CanCan | The Frontier Group Journal
サンプルがあまり多くないので、上記ふたつをよく読んでから

特にgem等は使わず自分でピュアRubyのオブジェクトを定義して入れる

ちょっと古めの記事ですが、以下の記事で出て来るPolicy Objectのことです。
7 Patterns to Refactor Fat ActiveRecord Models - Code Climate Blog

典型例は以下でしょうか
Policy Object – Medium

punditとpolicy objectがおなじpolicyでややこしいから、名前を変えたよという話?
Authorizers, Extractors, and Policy objects

policyというgemを使う

メンテされてなさそうですが、自分で作ったpolicy objectとcontrollerの橋渡しをしてくれるようなgemのようです
Rails - The Missing Parts - Policies | Grouper Engineering Blog

まとめ

今後の方針としては以下のような形で使い分けていきたい

  • アクションベースのシンプルな権限管理にはpundit
  • ロールベースの権限管理はcancancanを使う。app/policiesは必要になったら作る
  • 条件が複雑なロジック(アドやレコメンドを出すかどうかみたいな?)がたくさんある場合にはpolicy objectを作る。たくさん無いときはserviceとかmodelに入れれば良さそう

gemの使い方はgemに合わせるわけですが、そうでない場合はcallback、validators同様、そのロジックが業務上重要で複雑な場合は、早めにクラスをわけてメンテし易いように保ちましょう、という話でした。