CanCan:根据控制器数据或助手设置能力

时间:2013-08-07 13:16:27

标签: ruby-on-rails model session-variables cancan

所以我有以下设置:

class User < AR
  has_many :memberships
  has_many :user_groups, through: :memberships
  has_many :organizations, through: :memberships
end

class Membership < AR
  belongs_to :user
  belongs_to :user_group
  belongs_to :organization
end

class UserGroup < AR
  has_many :memberships
  has_many :users, through: :memberships
  has_many user_groups, through: :memberships
end

因此,一个用户可以是不同组织中多个用户组的成员,即他/她可以是组织A中的产品经理,组织B中的文章管理员和评论经理。

所以为了能够问can? :manage, an_article_instance我需要设置这样的能力:

class Ability
  if user.is_content_manager_for(currently_selected_organization)
    can :manage, Article

  elsif user.is_admin_for(currently_selected_organization)
    can :manage, User, memberships: { organization_id: currently_selected_organization }
  end
end

后端的Web界面应该有一个选择菜单,用户可以在其中选择他/她想要在哪个组织上工作。所以我在考虑将当前选定的组织存储在会话中,因为它是整个工作会话中的持久数据。

但是在Ability(或任何其他)模型中访问currently_selected_organization(或直接会话)之类的辅助方法实际上会违反MVC模式。我在几个地方看过这不好等等。

所以我想知道是否有更好/更清洁的方法呢?

1 个答案:

答案 0 :(得分:4)

好的,所以我用不同的方法弄明白了。 CanCan向current_ability添加ActionController::Base方法,该方法实例化新的Ability对象并将current_user对象传递给它。可以简单地在自己的控制器中覆盖current_ability方法,所以我最终做到了这一点:

class ApplicationController < ActionController::Base
  def current_ability
    @current_ability ||= Ability.new(current_user, Organization.find_by(id: cookies[:current_organization])
  end
end

class Ability
  include CanCan::Ability

  def initialize(user, currently_selected_organization)
    if user.is_admin_for(currently_selected_organization
      # ...
    end
  end
end

通过这种方式,它不会破坏MVC模式,我最终不会包含帮助模块或破解任何东西。