I have the following relationship between my classes user and page.
class User < ApplicationRecord
has_and_belongs_to_many :pages
end
class Page < ApplicationRecord
has_and_belongs_to_many :users
end
How can I do in my Ability file for a user to only edit the pages that belong to him?
class Ability
include CanCan::Ability
def initialize(user)
if user.is? :page_administrator
can :manage, Page
end
end
end
I try the following, but I still can not.
can :manage, Page, users: { user_id: user.id }
The biggest con with has_and_belongs_to_many
is that it makes it impossible to attach data to the join table. Instead use a join model:
class User < ApplicationRecord
has_many :user_pages
has_many :pages, through: :user_pages
end
class Page < ApplicationRecord
has_many :user_pages
has_many :users, through: :user_pages
end
class UserPage < ApplicationRecord
belongs_to :user
belongs_to :page
end
This works like has_and_belongs_to_many
except its not headless - you can query UserPage
directly. The only thing you need to do besides creating the UserPage model is renaming the table from users_pages
to user_pages
(or pages_users
to page_users
).
class RenameUsersPages < ActiveRecord::Migration[5.0]
def change
rename_table('users_pages', 'user_pages')
end
end
This is needed since rails will link the table to the constant Users::Page
otherwise.
Now you can easily attach a admin
flag on the UserPage
table.
class AddPageAdministratorToUserPages < ActiveRecord::Migration[5.0]
change_table :users do |t|
t.boolean :admin, default: false
end
end
Now we can check if a user is an admin by checking if a record exists in user_pages
:
class Ability
include CanCan::Ability
def initialize(user)
can :manage, Page do |p|
p.user_pages.exists?(admin: true, user: user)
end
end
end