我使用state_machine取得了巨大的成功,并且喜欢它通过几行代码动态创建的类方法。
但是,我不确定如何继续使用我正在创建的系统。我目前正在开发一个用户有很多角色的系统。因此,让用户的状态从未经证实到已确认然后可能管理员并非如此简单。
用户现在有很多角色,可以是潜在,骑车人, coorinator , manger ,论坛管理员,商店管理员,超级管理员和筹款人。
所以层次结构是这样的:
超级管理员
论坛管理员,商店管理员
骑单车,协调员,经理,筹款人
潜在
但是,有一台状态机不会在这里删除它,因为一个用户可以完全同时拥有上述所有角色。
我正在实现我自己的类方法,以便在某种程度上模拟状态机:
class User < ActiveModel::Base
has_many :jobs
has_many :roles, through: :jobs
def role_array
self.roles.pluck(:role)
end
def has_role?(role)
role_array.include?(role)
end
# checking
def is_superadmin?
role_array.include?('superadmin')
end
# changing
def add_role(role)
self.update_attributes(accepted_at: Time.now) if self.is_only_potential?
self.user_roles.create(role_id: Role.find_by(role: role).id ) if !self.has_role?(role)
end
def remove_role(role)
self.user_roles.find_by( role_id: Role.find_by(role: role).id ).destroy if self.has_role?(role)
end
def make_superadmin!
add_role('superadmin')
end
def denounce_superadmin!
remove_role('superadmin')
end
end
这只是一点点。所以我的问题是:
1)我做得对吗?您将如何处理具有多个角色的用户?
2)即使我做得对,我也想创建一个state_machine-esque DSL,所以当我需要创建一个新角色时,让我们说&#39; runner&#39;,我可以在我的模型中做这样的事情:
class User < ActiveModel::Base
has_many :jobs
has_many :roles, through: :jobs
multiroles initial: :potential do
roles [:superadmin, :forum_admin, :store_admin, :cyclist, :coordinator, :manager, :fundraiser, :potential]
# dynamically creates the above methods for getting and setting for all roles
end
我应该如何创建多重方法?在lib
内?准备好作为我的第一个宝石打包?
我不知道如何动态创建方法,但我想开始:)
只是想一想,也许multiroles
方法可以通过Roles.all
动态获取所有角色,并自动添加上述方法!甚至可以照顾has_many :jobs
has_many :roles, through: :jobs
另外,我应该如何验证这些角色?我目前在控制器的前一块中执行此操作:
def only_superadmins
redirect_to root_url if !current_user.has_role?('superadmin')
end
我的应用程序控制器only_superadmins
,only_cyclists
中也有一堆这样的方法,我在各个子控制器中通过before_method
方法调用它们。
这可以吗?我应该使用cancan还是什么?
如果我做得对,我想知道如何使用我的Gem动态创建这些方法。我正在思考这些问题:
class panel_controller < ApplicationController
allowed_roles [:super_admin, :forum_admin, :store_admin]
end
并且allowed_roles方法将创建这些方法
def allowed_roles(role_array)
role_array.each do |role|
define "only_#{role.to_s}s" do |arg|
redirect_to root_url if !current_user.has_role?(arg.to_s)
end
end
end
所以这会以编程方式创建这些方法:
def only_super_admins
redirect_to root_url if !current_user.has_role?('super_admin')
end
def only_forum_admins
redirect_to root_url if !current_user.has_role?('forum_admin')
end
def only_store_admins
redirect_to root_url if !current_user.has_role?('store_admin')
end
虽然我不明白为什么那不起作用,但这并不会让我觉得效率太高。
也许allowed_roles
应该是这样的:
def allowed_roles(wanted_roles)
redirect_to root_url unless (current_user.role_array & wanted_roles).empty? # it's ONLY empty when any of the current_user roles exists in the wanted_roles array
end
我只是想要一些指示:)
如何创建gem以使控制器可以使用allowed_roles
方法并且用户模型可以使用multiroles
?
可以cancan
管理这样的多个角色吗?我应该只使用它吗?
答案 0 :(得分:17)
恢复回答:
为了处理模型的角色,一个很好的选择是使用gem rolify 。有了它,您可以轻松定义所需的任意角色,并将您想要的多个角色关联到您的用户。它使用简单,只需遵循官方文档here。
CanCan (或其成员 CanCanCan )用于处理权限。您将定义具有每个角色的用户(使用rolify定义)在文件app/models/ability.rb
中有权执行的操作。然后,在控制器或视图中,您只需验证用户是否有权对资源执行操作。例如,在您的控制器中,您可以验证@comment = Comment.new(params); authorize! :create, @comment
之类的授权,并在您的视图中验证授权,例如if can? :create, Comment
。请参阅官方文档here,了解如何设置和使用CanCan。
针对您的具体问题:
将Rolify(gem "rolify"
)和CanCan(gem "cancan"
)宝石添加到您的Gemfile中。
执行rails shell命令rails g rolify Role User
以创建名为Role的新类(或使用您喜欢的名称),并在现有类User中添加一些类方法。由于新类Role将向数据库添加表Role,因此必须运行rake db:migrate
(使用ActiveRecord时)。
将resourcify
添加到用户将访问的任何类。例如:
class Forum < ActiveRecord::Base
resourcify
end
完成这些步骤后,您的用户类将配备方法add_role
,remove_role
和has_role
,您可以使用它们添加任意数量的角色:
user.add_role :superadmin
user.add_role :fundraiser
user.has_role? :superadmin
# >> true
user.has_role? :fundraiser
# >> true
您甚至可以将角色范围限定为一个资源或实例:
user.add_role :cyclist, Forum
user.add_role :coordinator, Forum.first
user.has_role? :cyclist, Forum
# >> true
user.has_role? :cyclist, Store
# >> false
user.has_role? :coordinator, Forum.first
# >> true
user.has_role? :coordinator, Forum.second
# >> false
所以你可以像这样编写你的用户类:
class User < ActiveModel::Base
rolify
has_many :jobs
# checking
def is_superadmin?
self.has_role?('superadmin')
end
# changing
def add_new_role(role)
self.update_attributes(accepted_at: Time.now) if self.is_only_potential?
self.add_role(role)
end
def make_superadmin!
add_new_role('superadmin')
end
def denounce_superadmin!
remove_role('superadmin')
end
end
要验证这些角色,您可以使用CanCan。执行rails shell命令rails g cancan:ability
以生成文件app/models/ability.rb
,您将在其中定义角色的权限。
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user
if user.has_role? :superadmin
can :manage, All # can manage (Read, Create, Update, Destroy, ...) everything
elsif user.has_role? :forum_admin
can :manage, Forum # can manage (Read, Create, Update, Destroy, ...) any Forum
elsif user.has_role? :store_admin
can :manage, Store do |store| # Can manage only its own store
store.try(:user) == user
end
elsif user.has_role? :forum_member
can :create, Post do |post|
if post.forum.members.include? user
true
end
end
can :destroy, Post do |post|
post.try(:user) == user
end
can :update, Post do |post|
post.try(:user) == user
end
elsif ...
else # Users without role
can :read, All
end
end
end
在您的控制器中,您可以调用authorize!
方法。例如:
# app/controllers/posts_controller.rb
def create
@post = Post.new(params[:post])
@post.user = current_user
authorize! :create, @post
if @post.save
redirect_to @post
else
render :action => 'new'
end
end
或者您可以在控制器的开始处包含faloowing,并且在每个操作之前资源会自动加载和授权(或不授权):
class PostController < ApplicationController
load_and_authorize_resource :post
...
def create
authorize! :create, @post
if @post.save
redirect_to @post
else
render :action => 'new'
end
end
在 RailsCast 上观看本教程,了解CanCan。
我希望这有助于指导您解决问题。