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.
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 end
In an ApplicationController before_action callback, we set the current user:
before_action :initialize_component_context def initialize_component_context Current.user = current_user end
In our view component, we can then access the user via the new class:
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.