Rails CanCan gem refactoring Ability class
Asked Answered
S

3

11

I have a around 13 models in my rails app, I use ability on all of them. My ability class has grown huge. I have different ability conditions on different CRUD actions which is making it hard to manage.

Can someone guide me on how I can refactor this..? Like, using modules or classes for making my ability class look neat.

Serviceberry answered 20/3, 2014 at 11:0 Comment(0)
S
18

Simple escenario: If you can split the permissions in several mutually exclusive sets, then you should check this proposal from @ryanb, CanCan creator, in which he splits the abilities into several different classes and then overwrites the current_ability method in the ApplicationController

How you can break up large Ability class in CanCan - Gists

More complex scenario: if you have several different overlapping permissions sets, you can try this approach:

# app/models/ability.rb

class Ability
  include CanCan::Ability

  def initialize(user)
    self.merge Abilities::Everyone.new(user)

    if user
      self.merge Abilities::Admin.new(user) if user.admin?
      self.merge Abilities::Authenticated.new(user)
    else
      self.merge Abilities::Guest.new(user)
    end
  end
end

# app/models/abilities/admin.rb
module Abilities
  class Admin
    include CanCan::Ability

    def initialize(user)
      # define abilities here ...
    end
  end
end

# app/models/abilities/everyone.rb
...

And so on for the rest of the files.

Sherlock answered 6/8, 2014 at 14:39 Comment(0)
E
3

Part of the solution mentioned here is really appealing. I would like propose an alternate method for handling this where we override the current_ability method in application_controller to make it dynamic depending on the controller being used. From there we can specify the ability to use in each controller. It might look something like this:

./app/abilities
              ./posts_ability.rb
              ./comments_ability.rb
              ./admin_ability.rb
              ./pictures_ability.rb
              ./uploads_ability.rb

Then in ./app/controllers/my_controller.rb it would look like this:

class MyController < ApplicationController
    authorize_with PostsAbility
end

It could also end up being automatic where the PostsController would use the PostsAbility by default.

In retrospect though, there is one thing to consider. It seems there are two ways to try to scale the abilities. In one way, we might have many "roles" that are allowed to interact with the data in different ways. The other way is that we have many models and need a way to split the logic up there. This approach works well with both of them because you can split the logic out based on the action that is being (or likely to be) taken.

One other thing, we can also use inheritence to pre-load abilities that are co-dependent. If Comment belongs to Post, then CommentsAbility could inherit from PostsAbility to add the needed logic.

Employee answered 13/12, 2015 at 6:9 Comment(0)
R
-2

You can simply shift your ability class to database and manage all permissions over there and fetch before checking the ability of the current_user to perform an action ., It wound not take much time in moving the permissions to db.

Rycca answered 20/3, 2014 at 11:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.