所以我有以下设置:
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模式。我在几个地方看过这不好等等。
所以我想知道是否有更好/更清洁的方法呢?
答案 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模式,我最终不会包含帮助模块或破解任何东西。