Is it possible to use cancan with two ability class
Asked Answered
A

4

6

I'm using rails 3.0.9, cancan 1.6.7 and devise 1.4.8

I'm using two devise models(User and Admin) for different log-in and registration process

So I want to divide the abilities depend upon the logged-in user(resource), because there are more than 70 models and only 10 models are common for both type of users(here more than 50 models and views are only used by Admin users)

I want to implement two Ability class(UserAbility and AdminAbility) and the devise helper method current_user/current_admin should be passed to UserAbility/AdminAbility

Example:

In ApplicationController.rb file

    def current_ability
        if current_user
            @current_ability = UserAbility.new(current_user)
        elsif current_admin
            @current_ability = AdminAbility.new(current_admin)
        end
    end

From the above my questions,

  1. Is multiple ability class is possible in cancan, if possible then how to create it because I tried

    rails g cancan:user_ability

    but I got error as Could not find generator cancan:user_ability.

  2. How to choose the appropriate Ability class for the logged-in User/Admin.

  3. If a controller is accessed by both the User and Admin, then how can I get the currently logged-in User/Admin's object

Is there any other solution for this?

Any one please help to solve this

Aalborg answered 5/10, 2012 at 14:7 Comment(0)
S
14

...that said, you can use multiple ability models directly if you prefer:

class UserAbility
  include CanCan::Ability

  def initialize(user)
    can :read, :all
  end
end

class AdminAbility
  include CanCan::Ability

  def initialize(admin)
    can :manage, :all
  end
end

class ApplicationController < ActionController::Base
  # overriding CanCan::ControllerAdditions
  def current_ability
    if current_account.kind_of?(AdminUser)
      @current_ability ||= AdminAbility.new(current_account)
    else
      @current_ability ||= UserAbility.new(current_account)
    end
  end
end
Seguidilla answered 5/10, 2012 at 14:36 Comment(2)
yes i tried for this only, but I tried lot to create another cancan Ability class but I can't (may be i don't know the right syntax)Aalborg
You don't need a generator -- just make a new file in app/models and include CanCan::Ability.Seguidilla
S
7

You don't need multiple ability classes for that:

class Ability
  include CanCan::Ability

  def initialize(user_or_admin)
    user_or_admin ||= User.new

    common_rules(user_or_admin)

    if user_or_admin.kind_of? Admin
      admin_rules(user_or_admin)
    else
      user_rules(user_or_admin)
    end
  end

  def common_rules(user_or_admin)
    # can :verb, :noun
  end

  def admin_rules(admin)
    can :manage, :all
  end

  def user_rules(user)
    can :read, :all
  end
end

CanCan will ultimately be calling Ability.new() with either model, but that's perfectly fine, since you can check what kind of object you received. You can, of course, delegate to other objects if you prefer; it's all just Ruby.

Seguidilla answered 5/10, 2012 at 14:29 Comment(4)
Thanks @willglynn, I'll go for it and please tell me, is it possible to call a helper method which returns currently logged-in User/Admin's object every time in view or there is any other way to get this objectAalborg
That depends on the rest of your application. It's certainly possible, but I can't tell you how.Seguidilla
Classy solution. NeatTrichosis
If your single ability.rb file gets unwieldy, take a look at separating them out into individual files, as shown here: #7738876Exudate
I
1

I've implemented part of @willgiynn's solution, modifying the current_ability methot in the application_controller.rb.

On ability.rb:

class UserAbility
  include CanCan::Ability

  def initialize(user)
    can :read, :all
  end
end

class AdminAbility
  include CanCan::Ability

  def initialize(admin)
    can :manage, :all
  end
end

On application_controller.rb

def current_ability
  @current_ability ||= current_admin ? AdminAbility.new(current_admin) : UserAbility.new(current_user)
end

Hope it helps!

Icelandic answered 10/5, 2020 at 21:31 Comment(0)
L
0

I will add to what willgiynn has answered. It seems that I had to add some more code to make it work. I found the answer from this post

Add the following to the application_controller.rb

  def current_ability
    if admin_signed_in?
      @current_ability ||= Ability.new(current_admin)
    else
      @current_ability ||= Ability.new(current_user)
  end
end
Lichee answered 14/6, 2015 at 8:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.