How to get Pundit to work with Rails View Component
Rails Pundit View Component

Published on 02/10/2021 by Jerry Weyer

In we use Pundit as our authorization system and Devise for authentication. This allows us to assign each signed-in user a role - admins can do everything, read-only user can only read.

I also included View Component in this project as I noticed in previous apps that it is very difficult to maintain a coherent design over the years if the html and css for each component (e.g. a button or dropdown menu) is not centralized.

After setting up Pundit and creating policies for each user role, we can hide or show different elements based on the current user's role - f.ex. the button to add a new team member will only be visible to admins.

In a view we can simply verify if policy(User).create? and Pundit will call the current_user method to check if the signed-in user is allowed to perform this action.

In a view component, this does not work out of the box however, as a component is generally agnostic about the view context.
As such policy(current_user, User).create? will give you an error:

  undefined method `current_user' for nil:NilClass

Several solutions to this problem are described in issue#310 (View Component). I prefer the CurrentAttributes api solution described by jaredcwhite.

In order to get our current_user to our components, we take advantage of ActiveSupport::CurrentAttributes:

  class Current < ActiveSupport::CurrentAttributes
    attribute :user

In an ApplicationController before_action callback, we set the current user:

  before_action :initialize_component_context

  def initialize_component_context
    Current.user = current_user

In our view component, we can then access the user via the new class:

  Pundit.policy(Current.user, Offer).edit?

In the issue comments, you can read more about how to make it work for previews as well. As I'm not using previews at the moment, I didn't include all the recommendations from the post.

Please head to the article if you want to comment or discuss this post.