This is a follow-up question on Rails 4: CanCanCan abilities with has_many :through association and I am restating the problem here since I believe context has slightly changed and after 4 updates, the code from the initial question is pretty different too.
I also checked other questions, like Undefined method 'role?' for User, but it did not solve my problem.
So, here we go: I have three models:
class User < ActiveRecord::Base
has_many :administrations
has_many :calendars, through: :administrations
end
class Calendar < ActiveRecord::Base
has_many :administrations
has_many :users, through: :administrations
end
class Administration < ActiveRecord::Base
belongs_to :user
belongs_to :calendar
end
For a given calendar
, a user
has a role
, which is defined in the administration
join model (in a column named role
).
For each calendar
, a user
can have only one of the following three role
s: Owner, Editor or Viewer.
These roles are currently not stored in dictionary or a constant, and are only assigned to an administration
as strings ("Ower", "Editor", "Viewer") through different methods.
Authentication on the User
model is handled through Devise
, and the current_user
method is working.
In order to only allow logged-in user
s to access in-app resources, I have already add the before_action :authenticate_user!
method in the calendars
and administrations
controllers.
Now, I need to implement a role-based authorization system, so I just installed the CanCanCan
gem.
Here is what I want to achieve:
user
s can create
new calendar
s.user
is the owner
of a calendar
, then he can manage
the calendar
and all the administration
s that belong to this calendar
, including his own administration
.user
is editor
of a calendar
, then he can read
and update
this calendar
, and destroy
his administration
.user
is viewer
of a calendar
, then he can read
this calendar
, and destroy
his administration
.To implement the above, I have come up with the following ability.rb
file:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.role?(:owner)
can :manage, Calendar, :user_id => user.id
can :manage, Administration, :user_id => user.id
can :manage, Administration, :calendar_id => calendar.id
elsif user.role?(:editor)
can [:read, :update], Calendar, :user_id => user.id
can :destroy, Administration, :user_id => user.id
elsif user.role?(:viewer)
can [:read], Calendar, :user_id => user.id
can :destroy, Administration, :user_id => user.id
end
end
end
Now, when log in and try to visit any calendar page (index, show, edit), I get the following error:
NoMethodError in CalendarsController#show
undefined method `role?' for #<User:0x007fd003dff860>
def initialize(user)
user ||= User.new
if user.role?(:owner)
can :manage, Calendar, :user_id => user.id
can :manage, Administration, :user_id => user.id
can :manage, Administration, :calendar_id => calendar.id
I guess the problem comes from the fact that a user
does not have a role
per se, but only has a role
defined for a given calendar
.
Which explains why I get a NoMethodError
for role?
on user
.
So, the question would be: how to check a user
role
for a given calendar
?
Any idea how to make things work?
You should have role?
method in user model, like below -
class User < ActiveRecord::Base
has_many :administrations
has_many :calendars, through: :administrations
def role?(type)
administrations.pluck(:role).include?(type.to_s)
end
end