読者です 読者をやめる 読者になる 読者になる

【Rails】権限管理のgem「pundit」を使ってみたときのまとめ

目的

punditというgemを使う際に「Rubyist Magazine - 権限管理のgem、Punditの紹介」を参考に作ったときのまとめ。

railsapp/policiesディレクトリって何を入れるのかというところを前回調べたのですが、代表的なものとしてpunditというgemを使うと出て来るというのが代表的な例なようなので、使ってみました。
【Rails】policiesディレクトリの使い方 - ITの勉強をいろいろやってみたブログ

るびまを参考にして作ったときに考えていたこと等をまとめてみます。

まとめ

ApplicationPolicyのサブクラスの初期化時には、recordではなくclassを渡す

rails g pundit:installを実行すると、app/policies/application_policy.rbが作られるのだが、初期実装は以下のようになっている。

class ApplicationPolicy
  # ...
  def initialize(user, record)
    @user = user
    @record = record
  end
  # ...
end

これだと、レコードを渡すことで「自分のレコードは更新できる。他の人のレコードは閲覧だけ。」みたいなレコード単位の権限をつけられる。
それはそれで便利なのだけど、社内用の管理画面等はレコード単位ではなくクラス・モデル単位での権限がついていれば十分。なので、以下のようにクラスを受け付ける想定にしておいて、その他も適宜書き換える。(モンキーパッチを当てる必要はなし)

class ApplicationPolicy
  # ...
  def initialize(user, klass)
    @user = user
    @klass = klass
  end
  # ...
end

ApplicationControllerには以下のようなメソッドを準備しておく。

class ApplicationController < ActionController::Base
  # ...
  def pundit_auth
    authorize controller_name.classify.constantize
  end
end

UsersControllerbefore_actionを呼ぶ

class UsersController < ApplicationController
  before_action :pundit_auth
  # ...
end

すっきり。

cancancan vs punditはRSpec vs minitestと似ている気がする

cancancanはAbilityというクラスを定義して、その中にDSLをわーっと書いて権限設定を定義します。
それに対しpunditはピュアなObjectを継承したpureなRubyのクラスで定義し、コントローラで呼ぶためのメソッドを提供しているだけです。

このような観点の比較はRSpec vs minitestのときの議論と似ているかなと思っています。
MinitestとRSpec、FixturesとFactoryGirlの良いところ悪いところをコードを書いて比較してみた - give IT a try

RSpec vs minitestのときはテストコードの話なのですが、権限は実際のアプリケーションの設計に関わってくる所なので、妥協したりプラグインに引っ張られた設計になるのは避けたいところです。
複雑な仕様がある場合にはcancancanにモンッキーパッチをあてる等して使うより、自由に書けるpunditを使っておくのも悪くない選択かなと思いました。

TODO

  • cancancanで同じことをやる
  • 認証はdeviceを使ってやったが、自前で実装してみる