Pundit for Roles on Rails

Roles are almost always a requirement for a web app. There are many good options out there for Rails, with the old guard being CanCan and Rolify. But then I met this newcomer, Pundit, and its simplicity stole the show.

The Rails 3 goto was CanCan, and I loved it. CanCan worked great with Devise and helped encapsulate everything role related into Abilities.

Pundit encapsulates authorization and scoping through Policies, using pure ruby classes and amazingly intuitive convention over configuration.

This does mean that you will have to add admin (or whatever you need) columns manually, but the simplicity of it all will save you in the long run.

Policy

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

Usage in Controller

def update
  @post = Post.find(params[:id])
  authorize @post
  @post.update_attributes(post_params)
  respond_with @post
end

Notice that the Controller#update action will automatically invoke Policy#update? by convention.

Sure, this will work for actions on individual members, like update. But what about index? That’s where scoping comes in:

Scope

class PostPolicy < ApplicationPolicy
  class Scope
    attr_reader :user, :scope

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

    def resolve
      if user.admin?
        scope.all
      else
        scope.where(:published => true)
      end
    end
  end

  ...
end

Usage in Controller

def index
  @posts = policy_scope(Post)
end

The snippets here were ripped off the GitHub page, check it out for yourself. It’s a fantastic gem.

comments powered by Disqus