2

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 }
Emília Parsons
  • 135
  • 1
  • 6

1 Answers1

4

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
max
  • 96,212
  • 14
  • 104
  • 165
  • You may want to check out the [rolify gem](https://github.com/RolifyCommunity/rolify) which provides a framework for role based authorization across many classes. – max Jan 19 '17 at 03:03