使用 Rolify 定义角色

Defining Roles with Rolify

我正在尝试使用 Rails 4.

制作一个应用

我正在查看角色 management 并想使用 Rolify,因为它支持实例级角色分配。

对于其他关注相同问题的人,下面有 2 个非常好的答案(我只能勾选一个,但我同时使用了两个)。查看下面的 lorefnon 和 Cyb3rDud3 答案)。我仍在弄清楚,但是已经使用数组(如 lorefnon 所示)和 controller/routes 函数(如 Cyb3rDud3 所示)进行了迁移。

完全让我感到困惑的是,Rolify gem 的所有文档都使用控制台来定义角色。

如何在我的代码中定义角色?

此板上的其他人提出的问题暗示他们在 db:seeds 文件中定义角色。我不想那样做,因为我想比谁可以创建角色更严格地控​​制谁使用我的种子文件。

你在哪里做的?

所有示例都显示它是从控制台完成的。我想定义一个角色列表,然后我想给角色权限(我想在这部分使用专家)。

我有一个用户模型。我看的另一个gem是榜样。它要求您在用户模型中创建一组角色。很明显,您应该在 Rolify 中执行此操作 - none 的文档为您提供了这一步?

你在哪里定义角色?

阅读他们项目页面中的文档和示例后,我决定不使用 gem 来管理我页面中的角色,因为我认为配置和使用它会花费我很多时间。因此,我改为执行以下操作(我相信您为用户模型使用了设计,即使它不是强制性的):

如果您想定义某些角色并且"static"不可修改但可从您的页面分配,请参见下文,如果不能则跳转到下一个粗体行

  1. 通过迁移将名为 role: integer 的字段添加到您的 User 模型。 (我们使用整数因为这个值将与我们在下一步定义的枚举中的一个条目相关联)
  2. 在您的文件 user.rb(模型)中,添加一个 enum,如下所示:

    class User < ActiveRecord::Base
      devise :registerable, #...
    
      enum role: [:admin, :normal, :premium, :moreRolesHere ]
      after_initialize :set_default_role, :if => :new_record?
    
      def set_default_role
        self.role ||= :normal
      end
    
    end
    
  3. 然后在任何控制器或视图或 def 中,您只需获取当前用户或您想要分配角色的任何用户,并像下面的行一样简单:

    #let's suppose we want to make premium the current user
    current_user.premium!
    
    #or someone else to be admin
    user = User.first
    user.admin!
    
  4. 然后您可以在您正在处理的任何页面或控制器中进行自己的验证,只需询问用户的角色即可:

    #see if the current user is admin
    if current_user.role == "admin"
      #do some admin stuff
    end    
    

如果您想在页面中添加、修改和删除角色并在其中分配角色

首先,直接从页面分配角色时要小心。这里的问题是每个人都可以分配自己在某个领域中选择的角色。但如果这是您的需要,请按以下步骤操作:

  1. 创建一个名为 Role 的模型,其中包含一个字段 role: string
  2. 创建与该模型关联的控制器,roles_controller.rb
  3. 创建您需要的视图以显示与角色的 management 相关的操作(创建、编辑、删除)请注意,此页面必须仅可供登录用户使用。为您将在用户控制器或您想要的任何其他控制器中管理的用户选择或添加角色。
  4. 在您想要询问用户角色的任何其他视图中,您将需要访问角色 table 并检索与用户对应的角色。 用户 table 需要一列 role_ids: text(这是文本,因为您需要将多个角色保存在那里,所有角色都包含在一个数组中),它将代表他的角色。在您的 user.rb 模型中,您可以使用 get_roles 和其他 defs 方法在控制器和视图中使用更简洁的代码:

    class User < ActiveRecord::Base
      devise :registerable, #...
    
      serialize :role_ids
    
      #will return you an array of roles-(strings) of the user
      def get_roles
        roles = []
        role_ids.each do |role_id|
          roles << Role.find(role_id).role
        end
        return roles      
      end
    
      #ask if this user has some role-(string)
      def has_role(role)
        roles = get_roles
        return roles.include?(role)
      end
    
    end
    
  5. 最后,您当然需要为角色、创建、更新和销毁以及与其视图关联的所有 def 实现控制器。

你可以看看how to save arrays in database, serialize

此方法不使用任何与角色法力gement 或授权相关的 gems,如 market 中的那些:pundit, cancan, rolify.如果您对我的方法持怀疑态度并想亲自动手,请留下一些链接。

What is completely baffling me is that all the documents for the Rolify gem use the console to define roles.

Rolify 文档不使用控制台来定义角色 - 它演示了如何在纯 ruby 中添加角色。这是惊人的强大,因为你可以 尽可能定义角色 运行 ruby。您不限于某些配置文件或某些数据库中定义的静态角色列表 table.


您需要问的第一个问题是角色何时创建?

最常见的用例分为两组:

1.角色是静态的。

角色由应用程序开发人员、支持人员或公司高管在应用程序 installation/deployment 期间创建一次,并且在 运行ning 应用程序的生命周期中,这些预先创建的角色被分配给不同的用户。

此类角色最常见的用例包括公司人员的建模指定(开发人员、经理、支持人员等),或建模先前已知的职责(编辑、管理员、查看者等)

如果您的角色确实属于此类用例,那么接下来您必须决定 - 由谁负责创建和修改角色。通常有两种可能性:

1.1. 应用程序开发人员本人是必须 add/remove/modify 角色的人。在这种情况下,最好依靠种子数据或 Rails 迁移。

迁移的优点是您可以在需要时轻松回滚数据。此迁移是对 rolify 生成器生成的迁移的补充,它为您创建角色相关 table 的模式(请参阅下图)。

这样的迁移可能看起来像:

db/migrate/20151204083556_create_application_roles.rb

class CreateApplicationRoles < ActiveRecord::Migration
  def up
    ['admin', 'support', 'editor'].each do |role_name|
      Role.create! name: role_name
    end
  end
  def down
    Role.where(name: ['admin', 'support', 'editor']).destroy_all
  end

end

有些人 rightly consider it an antipattern to have schema changes and data changes both managed by migrations. data-migrate 是 gem 允许您将以数据为中心的迁移与架构迁移分开。

在这种情况下以及下面的所有其他情况下,实际角色分配将基于用户操作或应用程序事件通过 rolify.This 提供的 add_roleremove_role 方法发生。 运行ning 应用程序的生命周期过程,而不是在应用程序安装期间。

1.2 adding/removing/modifying 角色的任务由支持团队或技术主管完成。在这种情况下,需要提供用于管理角色的管理界面。

在这种情况下,您将有一个 rails 控制器来管理角色。 create 动作将用于创建角色,show 动作将在那里展示角色等。这些动作将附带视图,为最终用户提供图形用户界面以管理角色。

2。角色是动态的

此类别涵盖的用例中,角色更像是类别或标签,最终用户可以 created/modified/deleted。例如,图书管理员可以将一些 role/category 分配给特定类型的书籍。

这种情况与 1.2 类似,因为您必须通过 rails 控制器来处理 creating/deleting/updating 角色。


下一部分是信息在您的 table 中的结构。

Rolify 需要一个特定的模式(在一定程度上可定制),但预期的模式足够灵活,可以处理上述所有用例。

我确实经历了同样的过程,就像@user2860931 一样,我所能找到的只是一些关于如何从控制台分配角色的示例。我需要的是一种灵活的编程方式,让具有管理员角色的用户或具有 pmo 角色的用户如何将这些角色分配给其他人。

经过一些试验,这就是我为我解决的方法。在此示例中,我使用 Devise 进行身份验证,并使用 Rolify 进行角色。

我假设您已经安装并运行了 Devise,因此您有一个现有的用户模型。按照 gem 页面上的说明安装 Rolify。我为角色模型使用了建议的名称 Role。所以按照此处所述执行所有操作:https://github.com/RolifyCommunity/rolify。安装 GEM,使用 rolify 生成角色用户。并迁移数据库迁移。

这将有效地为您提供新的 table 角色以及与用户 table 的 has_and_belongs_to_many 关系。

至于我的目的,我不需要通常的角色创建读取(显示)更新删除(CRUD)界面,我只是通过 seeds.rb 创建了一些像这样的。

#Seeding the Role table
#
p "Removing existing #{Role.all.count} roles"
Role.destroy_all
p "Creating 7 roles"
[:user, :admin, :portfolio_manager, :programme_manager,     :project_manager, :coordinator, :pmo].each do |role|
  Role.create( name: role )
end
p "Should have created 7 Roles, roles created: #{Role.all.count}"

我在处理开发内容时留下了额外的评论,因此我一眼就能看出它运行良好。所以当你 运行

耙子db:seed

您将设置一些角色。或者,您可以以通常的方式创建角色控制器和视图,以允许具有管理员角色的用户添加新角色。

现在可以开始有趣的部分了。到目前为止,Devise 已经完成了与您的用户有关的所有事情,或者您可能已经完成了自己的控制器,但您需要创建自己的用户控制器和视图。因为我只想要针对每个用户的角色的勾选框列表,所以我按如下方式完成了。这是我的

users_controller.rb

class UsersController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update]

  def index
    @users = User.all
  end

  def show
  end

  def edit
  end

  def update
    respond_to do |format|
      if @user.update(user_params)
        # TODO: Move hardcode flash message into language file
        format.html { redirect_to @user, notice: 'User was successfully updated.'}
        format.json { render :show, status: :ok, location: @user }
      else
        format.html { render :edit }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  private

  def set_user
    @user = User.find(params[:id])
  end

  def user_params
    params.require(:user).permit(:username, :email, {role_ids: []})
  end
end

现在您必须为它们定义路线,以免它们与 Devise 路线发生冲突。我暂时保持简单,并提供所有常用路由来强化您的应用程序,您可能想要更改它,只允许您实际拥有的路由。

routes.rb

Rails.appliction.routes.draw do
  devise_for :users
  root 'pages#home'
  resources :users    #must be after devise
end

当您现在执行 rake routes 时,您将获得应用程序使控制器工作的常用路径。像这样按顺序排列:

                  Prefix Verb   URI Pattern                      Controller#Action
        new_user_session GET    /users/sign_in(.:format)       devise/sessions#new
            user_session POST   /users/sign_in(.:format)       devise/sessions#create
    destroy_user_session DELETE /users/sign_out(.:format)      devise/sessions#destroy
           user_password POST   /users/password(.:format)      devise/passwords#create
       new_user_password GET    /users/password/new(.:format)  devise/passwords#new
      edit_user_password GET    /users/password/edit(.:format) devise/passwords#edit
                         PATCH  /users/password(.:format)      devise/passwords#update
                         PUT    /users/password(.:format)      devise/passwords#update
cancel_user_registration GET    /users/cancel(.:format)        devise/registrations#cancel
       user_registration POST   /users(.:format)               devise/registrations#create
   new_user_registration GET    /users/sign_up(.:format)       devise/registrations#new
  edit_user_registration GET    /users/edit(.:format)          devise/registrations#edit
                         PATCH  /users(.:format)               devise/registrations#update
                         PUT    /users(.:format)               devise/registrations#update
                         DELETE /users(.:format)               devise/registrations#destroy
             user_unlock POST   /users/unlock(.:format)        devise/unlocks#create
         new_user_unlock GET    /users/unlock/new(.:format)    devise/unlocks#new
                         GET    /users/unlock(.:format)        devise/unlocks#show
                    root GET    /                              pages#home
                   about GET    /about(.:format)               pages#about
                 contact GET    /contact(.:format)             pages#about
                   users GET    /users(.:format)               users#index
                         POST   /users(.:format)               users#create
                new_user GET    /users/new(.:format)           users#new
               edit_user GET    /users/:id/edit(.:format)      users#edit
                    user GET    /users/:id(.:format)           users#show
                         PATCH  /users/:id(.:format)           users#update
                         PUT    /users/:id(.:format)           users#update
                         DELETE /users/:id(.:format)           users#destroy

现在剩下要做的就是构建用户界面,这对我来说是最重要的部分,如果我对您的理解也正确的话。为了快速构建它,我的示例还没有很好地展示完整的 css 魔法,但我相信你可以按照自己的口味来做。

因此,要显示现有用户并从列表中挑选他们,请在您的 /app/views/users 文件夹中创建 index.html.erb。创建一个简单的 show.html.erb 和一个编辑,您可以在其中分配和删除现有角色。像这样。

index.html.erb

<!-- TODO: Tidy up this file and make it look good -->
<!-- TODO: Remove hard coded text to a locale file -->
<% @users.each do |user| %>
  <p>
    <%= link_to "#{user.username}<#{user.email}>", user %>
    <%= link_to "edit", edit_user_path(user) %>
  </p>
<% end %>

show.html.erb

<!-- TODO: Tidy up this file and make it look good -->
<!-- TODO: Remove hard coded text to a locale file -->
<p>
  Username: <%= @user.username %>
</p>
<p>
  Email address: <%= @user.email %>  
</p>

<%= link_to "Back", users_path %>

edit.html.erb

<!-- TODO: Tidy up this file and make it look good -->
<!-- TODO: Remove hard coded text to a locale file -->
<p>
 Username: <%= @user.username %>
</p>
<p>
 Email address: <%= @user.email %>
</p>

<%= form_for @user do |f| %>
  <% Role.all.each do |role| %>
    <%= check_box_tag "user[role_ids][]", role.id, @user.role_ids.include?(role.id) %>
    <%= role.name %></br>
  <% end %>
  <%= f.submit %>
<% end %>

<%= link_to "Back", users_path %>

这就是一个简单的用户界面,它列出了数据库中的所有可用角色,并提供了针对用户记录的复选框以启用或禁用此类角色。像这样:

Example of user record and role pick list

这也让我有点头疼,但希望这会让你走上增加逻辑和用户体验的道路。

@lorefnon 接受的答案确实包含迁移中的恶性反模式:

class CreateApplicationRoles < ActiveRecord::Migration
  def up
    ['admin', 'support', 'editor'].each do |role_name|
      Role.create! name: role_name
    end
  end
  def down
    Role.where(name: ['admin', 'support', 'editor']).destroy_all
  end
end

不是因为数据和架构原则的分离,而是因为我们应该永远在迁移中使用模型对象!

迁移是不可变的,但模型是可变的:如果您的迁移之一依赖于一个或多个模型 class,如果 class 之一被修改、重命名或已删除(例如,如果我们决定删除我们依赖项的 rolify)。

迁移应仅依赖 ActiveRecord::Migration 机制(add_column、create_tables、...)和纯 SQL。