認可を実装する時に使うpunditのコードリーディング

Punditとは

Pundit は認可を実装をする時によく使用されるgem。基本的な使用方法は今回省略。

まずが全体構成

├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── lib
│   ├── generators
│   │   ├── pundit
│   │   │   ├── install
│   │   │   │   ├── USAGE
│   │   │   │   ├── install_generator.rb
│   │   │   │   └── templates
│   │   │   │   └── application_policy.rb
│   │   │   └── policy
│   │   │   ├── USAGE
│   │   │   ├── policy_generator.rb
│   │   │   └── templates
│   │   │   └── policy.rb
│   │   ├── rspec
│   │   │   ├── policy_generator.rb
│   │   │   └── templates
│   │   │   └── policy_spec.rb
│   │   └── test_unit
│   │   ├── policy_generator.rb
│   │   └── templates
│   │   └── policy_test.rb
│   ├── pundit
│   │   ├── policy_finder.rb # このファイル
│   │   ├── rspec.rb
│   │   └── version.rb
│   └── pundit.rb # このファイル
├── pundit.gemspec
└── spec
├── policies
│   └── post_policy_spec.rb
├── policy_finder_spec.rb
├── pundit_spec.rb
└── spec_helper.rb

今回主に見るのはpundit.rbとpolicy_finder.rbです。

authorizeメソッド

使うときにはコントローラー内でinclude Punditしてauthorizeを呼ぶと思います。
なので
def authorizeでgrepしてみると2箇所ヒット。

# lib/pundit.rb 70行目あたり

これはReadmeでいうとこのあたりの話で、今回見たいケース(コントローラーにincludesして使う)ではないので飛ばします。

# lib/pundit.rb 207行目あたり

こちらがコントローラー内でinclude Punditして使うときのメソッドと思われます。
まず気づくのが引数にuserっぽいものがないこと。
代わりにpundit_userを呼んでおりpundit_userではcurrent_userを呼んでいます。
# lib/pundit.rb 312行目あたり

つまりcurrent_userというメソッドを予め定義しておくだけで良いことになります。deviseなどを使っていればたいていログイン中ユーザはcurrent_userというメソッドで取得できると思います。もちろんここをオーバーライドすれば任意のメソッドとしてユーザを取得することができます。

Readmeのこのあたりの内容と思われます。

話を戻して、次に第二引数のquery = nil の部分ですが、指定しなければ”#{action_name}?”となっているので、
indexアクション内でauthorize(record)とすると、対象のポリシークラスのindex?メソッドを実行することになります。
index?の結果がfalseかの場合NotAuthorizedErrorをrasieしているので、エラーハンドリングを行いたいときはこのエラーをcatchすれば良いこともわかります。
第三引数のpolicy_class: nilですが、
policy_class ? policy_class.new(pundit_user, record) : policy(record)
なので、指定した場合のみそのクラスをnewする仕組みのようです。指定がない場合は、policy(record)が実行されて、
# lib/pundit.rb 261行目あたり

さらにここを追っていくと最終的には

# lib/pundit/policy_finder.rb 73行目あたり

ポリシークラスを決定するロジックが書かれています。
if subject.is_a?(Array) ではjoin(“::”)は
ポリシークラスを階層化する機能を実現していると思われます。
あとはそれぞれ特定メソッドが定義されているかを優先順位をもとに確認していってポリシークラスを決定しています。
subject.respond_to?(:model_name)やsubject.class.respond_to?(:model_name)があるあたり、インスタンスを渡してもクラスを渡しても探してくれる感じになっています。
post = Post.find(1)
authorize(record)
とかすると、Post + SUFFIX でPostPolicyクラスを探してくれそうです。
(SUFFIX = “Policy”と定義されています。)

スポンサーリンク
スポンサーリンク
スポンサーリンク

シェアする

  • このエントリーをはてなブックマークに追加

フォローする

スポンサーリンク
スポンサーリンク