Punditはユーザの認可をシンプルに実装できるGemです。 認可に関するビジネスロジックを、実際の認可を行なうところ(コントローラやビュー)から分離できるため、可読性が上がるなどのメリットがあります。

この記事では、Punditの特徴や導入についてまとめてみました。

Punditの特徴

Punditは次のような特徴をもっています。 他の権限管理用のGemに比べてとにかくシンプルで分かりやすく、かつ実用上問題のないライブラリだと思います。

  1. リソースベースなので理解しやすい
  2. リソース単位でファイルを分割するという規約をもつため、必然的にクリーンになる
  3. 純粋なRubyのクラスとして書ける

Policyクラス

Punditでは、Policyという責務をつくり権限を管理します。 ファイルはapp/policiesに配置し、ファイル名はモデルのクラス名にPolicyという接尾辞をつけます。

たとえば、以下はPostモデルに関するPolicyです。

app/policies/post_policy.rb:

class PostPolicy
  attr_reader :user, :post

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

  def update?
    user.admin? || !post.published?
  end
end

#initializeuserは、コントローラのコンテキストにおける#current_userが渡されます。 また、2つ目の引数にはそのリソースのオブジェクトが渡されます。 この2つを元に権限を実装していきます。

また、Policyクラスではコントローラのアクション名に?をつけたメソッドを定義します(例:#update?)。

コントローラのアクション内で#authorizeを実行すると、これに?をつけたPolicyクラス内のメソッドが呼び出され、これを元に認可を行なうことになります。

認可

「そのユーザがその処理を行なう権限があるか?」という認可の処理は、コントローラとビューの2つのレイヤーで行なうことになると思います。 それぞれについてまとめます。

コントローラ

コントローラでは、#index#new#create#showなど、7つの基本アクションを定義すると思います。 この中で#authorizeを実行すると、対応するPolicyの?がついたメソッドを元に認可の処理が行なわれます。

たとえば以下は、PostsControllerの#updateメソッドにおける認可の例です。

class PostsController < ApplicationController
  def update
    @post = Post.find(params[:id])
    authorize @post
    ...
  end
end

ビュー

たとえば権限のあるユーザにだけ編集用のリンクを表示する場合など、ビューでも認可を行ないたいことがあります。 これは、ヘルパーメソッドである#policyをとおして行ないます。

<% if policy(@post).update? %>
  <%= link_to 'Edit', edit_post_path(@post) %>
<% end %>

参考記事

おわりに

Punditはドキュメントが分かりやすく、コード量もとても少ないので理解しやすいライブラリです。

複雑な認可を伴わないアプリケーションなら十分実用的だと思うので、ぜひ使ってみてください。