Вопрос

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.

Это было полезно?

Решение

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.

Другие советы

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.

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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top