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模式。我在几个地方读到这不好,等等。

所以我想知道是否有更好/更干净的方法来做这件事?

好的,所以我用不同的方法解决了这个问题。CanCan在ActionController::Base中添加了一个current_ability方法,该方法实例化了一个新的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模式,我最终也不会包含帮助器模块或破解任何东西。

最新更新