I have the user table (created by devise), I added a role column, which can be: admin, manager and agent. And in user.rb I put an ENUM like this:
class User < ApplicationRecord
enum role: { admin: 0, manager: 1, agent: 2 }
end
I'm using cancancan and would like to do 2 things:
1 - How to show a menu or a different button according to the registered user's role? In the view I put it like this:
<% if current.present? && current_user.admin? %>
<%= link_to 'Purchases (Admin access)', admin_purchases_path, class: 'nav-link mr-5 ' %>
<% end %>
2 - How to show a specific page according to the user's role? In my User.rb I put this:
def admin?
self.role == 'admin'
end
I'm trying it on ability.rb:
user ||= User.new # guest user (not logged in)
if admin?
can :manage, :all
else
can :read, :all
end
And on the controllers, I put:
load_and_authorize_resource
But I'm getting an error:
NoMethodError in Home#index
undefined method `admin?' for nil:NilClass
<% if current_user.admin? %> <<< On this line here
You can use the safe navigation operator to avoid a nil error:
<% if current_user&.admin? %>
However its still somewhat of an anti-pattern. The whole point of using an authorization library like CanCanCan is to consolidate the authorization logic instead of spreading it all over your controllers and views.
Your view should instead be asking CanCanCan if the user is authorized to do whatever it is that the link does:
<% if can? :administrate, :purchases %>
<%= link_to 'Purchases (Admin access)', admin_purchases_path, class: 'nav-link mr-5 ' %>
<% end %>
It doesnt have to know if the user is an admin or that admins are allowed to administrate purchases. Thats CanCanCans job.
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, :all
can :administrate, :all
else
can :read, :all
end
In my User.rb I put this ..
That code is completely reduntant. enum role: { admin: 0, manager: 1, agent: 2 }
already creates the interogration methods admin?
, manager?
and agent?
.
The problem with your ability.rb
is that you're calling if admin?
without actually specifying the recipient. You should be calling if user.admin?
.