Search code examples
ruby-on-railsdeviseassociationscancancanrolify

Associations with Roles using Rolify/Devise/Cancancan


I read somewhere using Rolify/Devise/Cancancan was a better alternative to configuring two devise models with login functionality (using one login page instead of two) and their respective associations between other models. I'm confused as to how to set roles within and still use associations. For instance:

If I had used two Devise models they would have just been...

class Supervisor < ApplicationRecord
  has_many :employees
end

class Employee < ApplicationRecord
  belongs_to :supervisor
end

But with Rolify I would like to do the following:

  1. Admin - this user should be able to set the roles of other Users
  2. Supervisor - (this user can set the employees schedule for example)
  3. Employee

Am I going about this wrong? I know the example is vague and I can't seem to find an answer anywhere on how to setup associations with roles.


Solution

  • What you're looking for is most likely a self-referential association:

    class AddSupervisorToUsers < ActiveRecord::Migration[6.1]
      def change
        add_reference :users, :supervisor, 
          null: true, 
          foreign_key: { to_table: :users }
      end
    end
    
    
    class User < ApplicationRecord
      belongs_to :supervisor, 
        class_name: 'User',
        optional: true,
        inverse_of: :employees
      has_many :employees,
        class_name: 'User',
        foreign_key: :supervisor_id,
        inverse_of: :supervisor
    end
    

    This is basically just an assocation that points back to the same table. Note that the foreign key column must be nullable to avoid a chicken vs egg scenario which would prevent you from creating any users (if the table was empty).

    While you could do this with Rolify by:

    user.add_role(:supervisor, User.first) # add a role
    User.with_role(:supervisor, User.first) # query for supervisors
    

    This isn't actually an equivilent as there is nothing that prevents multiple users from being the supervisor of a user. You also don't have a foreign key to ensure referential integrity as the assocation in the roles model is polymorphic.

    While Rolify is a great tool for some use cases you might not want to shove all your buisness logic into it.