I've literally just gone through the same process, and like @user2860931 all I could find was some examples on how to assign roles from the console. What I required is a programmatically flexible way on how a user with the admin role or user with say the pmo role can assign those roles to others.
With a little bit of experimenting, this is how I resolved it for me. In this example I am using Devise for authentication, and Rolify for the roles.
I assume you have Devise already installed and working, therefore that you have an existing User model. Install Rolify as per the instruction on the gem page. I used the proposed name Role for the roles model. So do everything as stated here: https://github.com/RolifyCommunity/rolify. Install the GEM, generate using rolify the Role User. And migrate the database migration.
This will effectively leave you with a new table Roles and a has_and_belongs_to_many relationship with the Users table.
As for my purpose I do not require the usual Create Read (show) Update Delete (CRUD) interface for the roles, I just created a few via the seeds.rb like this.
#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}"
I've left my additional commentary in as I do with the development stuff so I can see at a glance it worked fine. So when you run
rake db:seed
You'll have some roles setup. Alternatively you can create a roles controller and views in the usual way to allow say users with admin role add new roles.
Now the fun bit can begin. So far Devise has done everything regarding your users, or perhaps you've done your own controller anyway but you need to create your own users controller and views. As I want just a list of tick boxes for the roles against each user I've done it as follows. Here is my
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
Now you've got to define the routes for these such that they don't clash with the Devise routes. I've kept it simple for now and resourced all the usual routes for to harden your application you may want to change that and only allow routes for those that you actually have.
routes.rb
Rails.appliction.routes.draw do
devise_for :users
root 'pages#home'
resources :users #must be after devise
end
When you now do a rake routes you'll get the usual paths for your application to make the controller work. Like this and in that order:
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
All that is left to do now is build the user interface, the most important part to me, and if I understood correctly to you as well. In order to build this quickly, my example is not nicely presented yet with full css magic but I'm sure you can do that to your own taste.
So to show existing users and pick them of a list, create the index.html.erb in your /app/views/users folder. Create a simple show.html.erb and an edit in which you can assign and remove the existing roles. Like this.
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 %>
And there you have it, a simple user interface which lists all available roles from the database and provides tick boxes against a user record to enable or disable such a role. Like this:
Example of user record and role pick list
It was a little head scratcher for me as well, but hopefully this will put your on your way to increase the logic and user experience.