About two months ago I wrote an article on getting started with Devise and CanCan. Since then, I’ve implemented the Devise + CanCan combo on three projects and wrote a couple specs for Ryan Bates to help improve CanCan functionality. This article will focus more on Devise with some CanCan sprinkled in there.
If you read my first article on Devise + CanCan, you have some questions on your mind. The most common question was how to create a RESTful interface for a super admin to CRUD users. This is a common requirement for managing users and/or keeping registration private. This is really easy.
I’m going to show you how to keep the awesome functionality provided by Devise while adding your custom stuff with no major time cost!

Step 1 – Configure Routes
Since you probably want to keep your public interface for logging in and password recovery, leave your devise routes and add RESTful routes for users:
devise_for :users resources :users
Step 2 – The User Controller
Next you need to set up your CRUD actions in the controller. Most of the actions are typical:
class UsersController < ApplicationController before_filter :get_user, :only => [:index,:new,:edit] before_filter :accessible_roles, :only => [:new, :edit, :show, :update, :create] load_and_authorize_resource :only => [:show,:new,:destroy,:edit,:update] # GET /users # GET /users.xml # GET /users.json HTML and AJAX #----------------------------------------------------------------------- def index @users = User.accessible_by(current_ability, :index).limit(20) respond_to do |format| format.json { render :json => @users } format.xml { render :xml => @users } format.html end end # GET /users/new # GET /users/new.xml # GET /users/new.json HTML AND AJAX #------------------------------------------------------------------- def new respond_to do |format| format.json { render :json => @user } format.xml { render :xml => @user } format.html end end # GET /users/1 # GET /users/1.xml # GET /users/1.json HTML AND AJAX #------------------------------------------------------------------- def show respond_to do |format| format.json { render :json => @user } format.xml { render :xml => @user } format.html end rescue ActiveRecord::RecordNotFound respond_to_not_found(:json, :xml, :html) end # GET /users/1/edit # GET /users/1/edit.xml # GET /users/1/edit.json HTML AND AJAX #------------------------------------------------------------------- def edit respond_to do |format| format.json { render :json => @user } format.xml { render :xml => @user } format.html end rescue ActiveRecord::RecordNotFound respond_to_not_found(:json, :xml, :html) end # DELETE /users/1 # DELETE /users/1.xml # DELETE /users/1.json HTML AND AJAX #------------------------------------------------------------------- def destroy @user.destroy! respond_to do |format| format.json { respond_to_destroy(:ajax) } format.xml { head :ok } format.html { respond_to_destroy(:html) } end rescue ActiveRecord::RecordNotFound respond_to_not_found(:json, :xml, :html) end # POST /users # POST /users.xml # POST /users.json HTML AND AJAX #----------------------------------------------------------------- def create @user = User.new(params[:user]) if @user.save respond_to do |format| format.json { render :json => @user.to_json, :status => 200 } format.xml { head :ok } format.html { redirect_to :action => :index } end else respond_to do |format| format.json { render :text => "Could not create user", :status => :unprocessable_entity } # placeholder format.xml { head :ok } format.html { render :action => :new, :status => :unprocessable_entity } end end end ... end
CanCan provides the class level accessible_by method that I am using to retrieve all users that can be viewed by the current user. The load_and_authorize_resource filter provided by CanCan actually does this accessibility filtering for you to keep things DRY and it recognizes collections as of this issue fix. I support sorting in my actual code so I didn’t want to use the CanCan filter to grab my user objects in the index action.
The other two filters – get_user and accessible_roles – are pretty basic:
# Get roles accessible by the current user #---------------------------------------------------- def accessible_roles @accessible_roles = Role.accessible_by(current_ability,:read) end # Make the current user object available to views #---------------------------------------- def get_user @current_user = current_user end
Finally, respond_to_not_found is an application wide helper I use to respond when a requested object is not found. I stole it from the Fat Free CRM source code.
Next we’ll look at the Update action which has some non standard code.
# PUT /users/1 # PUT /users/1.xml # PUT /users/1.json HTML AND AJAX #---------------------------------------------------------------------------- def update if params[:user][:password].blank? [:password,:password_confirmation,:current_password].collect{|p| params[:user].delete(p) } else @user.errors[:base] << "The password you entered is incorrect" unless @user.valid_password?(params[:user][:current_password]) end respond_to do |format| if @user.errors[:base].empty? and @user.update_attributes(params[:user]) flash[:notice] = "Your account has been updated" format.json { render :json => @user.to_json, :status => 200 } format.xml { head :ok } format.html { render :action => :edit } else format.json { render :text => "Could not update user", :status => :unprocessable_entity } #placeholder format.xml { render :xml => @user.errors, :status => :unprocessable_entity } format.html { render :action => :edit, :status => :unprocessable_entity } end end rescue ActiveRecord::RecordNotFound respond_to_not_found(:js, :xml, :html) end
That was easy!
The only unusual code we added is to clean up the password request params if the user’s password field is blank, and validate the password with Devise’s valid_password? method if it’s not blank. That allows us to provide a UI that looks like this:

Step 3 – Add you views
It may seem a bit redundant to show all of my view code, but I’m going to do it anyway so you can see how I’m using CanCan everywhere.
index.html.erb
<!-- table header stuff here --> <% @users.each do |u| %> <tr> <td><%= link_to_if(can?(:read, User), "#{u.name}", user_path(u.id)) {} %></td> <td><%= "#{u.email}" %></td> <td><%= "#{u.roles.collect{|r| r.name}}" %></td> <td><%= link_to_if(can?(:edit, User), image_tag("/images/edit_icon.gif"), edit_user_path(u.id)) {} %></td> <td><%= link_to_if(can?(:delete, u), image_tag("/images/delete_icon.gif"), u, :confirm => "Are you sure?", :method => :delete) {} %></td> </tr> <% end %>
Note the heavy use of link_to_if(can?(:method, object), innerHTML, path, options) . It’s nice to only display links to those who can actually use them.
User registration – new.html.erb
<h2>Register User</h2> <%= form_for(@user) do |f| %> <%= error_messages(@user,"Could not register user") %> <%= render :partial => 'user_fields', :locals => { :f => f } %> <p><%= f.label :password %></p> <p><%= f.password_field :password %></p> <p><%= f.label :password_confirmation %></p> <p><%= f.password_field :password_confirmation %></p> <p><%= f.submit "Register" %></p> <% end %>
Where the partial – _user_fields.html.erb is the following:
<p><%= f.label :first_name %></p> <p><%= f.text_field :first_name %></p> <p><%= f.label :last_name %></p> <p><%= f.text_field :last_name %></p> <p><%= f.label :email %></p> <p><%= f.text_field :email %></p> <% if can? :read, Role %> <p><%= f.label :role %></p> <ul class="no-pad no-bullets"> <%= habtm_checkboxes(@user, :role_ids, @accessible_roles, :name) %> </ul> <% end %>
This is quite similar to the Devise generated views except we are not using a general “resource” object, we are specifying @user. Owning our views gives us the ability to easily add custom fields.
edit.html.erb
<h3><%= @user == @current_user ? "Your Account Settings" : "Edit User" %></h3> <%= form_for(@user, :html => { :method => :put }) do |f| %> <%= error_messages(@user,"Could not update user") %> <%= render :partial => 'user_fields', :locals => { :f => f } %> <p><%= f.label :password %> <i>(leave blank if you don't want to change it)</i></p> <p><%= f.password_field :password %></p> <p><%= f.label :password_confirmation %></p> <p><%= f.password_field :password_confirmation %></p> <p><%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i></p> <p><%= f.password_field :current_password %></p> <p><%= f.submit "Update" %></p> <% end %> <%= link_to "Back", :back %>
show.html.erb
<h3><%= @user.name %></h3> <%= link_to_if(can?(:update,@user), "Edit", edit_user_path(@user)) %> | <%= link_to_if(can?(:delete, @user), "Delete", user_path(@user), :confirm => "Are you sure?", :method => :delete) {} %> <table class="one-column-emphasis"> <tbody> <tr> <td class="oce-first">Email:</td> <td><%= @user.email %></td> </tr> <tr> <td class="oce-first">Role:</td> <td><%= @user.roles.first.name %></td> </tr> <% if can?(:see_timestamps,User) %> <tr> <td class="oce-first">Created at:</td> <td><%= @user.created_at %></td> </tr> <tr> <td class="oce-first">Last Sign In:</td> <td><%= @user.last_sign_in_at %></td> </tr> <tr> <td class="oce-first">Sign In Count:</td> <td><%= @user.sign_in_count %></td> </tr> <% end %> </tbody> </table>
Wait…WTF is “see_timestamps” ???
This is one thing I love about CanCan – it’s easy to add arbitrary permissions. In my CanCan ability class I can have:
#------------------------------------------------ def initialize(user) user ||= User.new # guest user if user.role? :admin can :see_timestamps, User elsif user.role? :normal can :see_timestamps, User, :id => user.id end end
I’ll admit that in my actual code I’m showing the timestamps to users that can? :manage, :all but you get the idea.
Between this post and my first post on Devise and CanCan, you should be rocking out.

If you enjoyed this post, make sure you subscribe to my RSS feed!










66 Comments
This works great. Can you demonstrate how to create a multi-select for the user’s user_role
Good stuff!
I would like to see how you implemented unit and functional tests
Hi, I was wondering what your habtm_checkboxes helper looks like? Thanks!
New link for the respond_to_not_found from Fat Free CRM is https://github.com/fatfreecrm/fat_free_crm/blob/master/app/controllers/application_controller.rb
Nice tutorial. I keep getting one error thorugh, undefined method `error_messages’. Is there a method missing?
to resolve the error_messages issue.in rails 3.1.3, I copied a file named error_messages_helper.rb in the /app/helpers/. Putting a copy of the content of that file here as well.
module ErrorMessagesHelper
# Render error messages for the given objects. The :message and :header_message options are allowed.
def error_messages_for(*objects)
options = objects.extract_options!
options[:header_message] ||= I18n.t(:”activerecord.errors.header”, :default => “Invalid Fields”)
options[:message] ||= I18n.t(:”activerecord.errors.message”, :default => “Correct the following errors and try again.”)
messages = objects.compact.map { |o| o.errors.full_messages }.flatten
unless messages.empty?
content_tag(:div, :class => “error_messages”) do
list_items = messages.map { |msg| content_tag(:li, msg) }
content_tag(:h2, options[:header_message]) + content_tag(:p, options[:message]) + content_tag(:ul, list_items.join.html_safe)
end
end
end
module FormBuilderAdditions
def error_messages(options = {})
@template.error_messages_for(@object, options)
end
end
end
ActionView::Helpers::FormBuilder.send(:include, ErrorMessagesHelper::FormBuilderAdditions)
@Tony, I followed both #1 and #2 tutorial as one whole complete app. all is well except I ran into a problem towards the end.
The problem is that when a super_admin user try to create a new user. I got error:
Started POST "/users" for 127.0.0.1 at 2012-03-09 23:37:51 -0500 Processing by RegistrationsController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"c8v6fmCFSlJV2v9qClxD46c1wcBU7n78Mk9xWsJm/Ls=", "user"=>{"email"=>"test@example.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "customer_attributes"=>{"first_name"=>"test", "last_name"=>"doe"}, "role_ids"=>["", "3"]}, "commit"=>"Sign up"} Completed in 9ms NameError (uninitialized constant Registration):What I can figured out is the route conflict. tutorial #1, use devise custom registrations controller for registering new user. tut#2, added another way to create a new user by super_admin. Below is route listing.
cancel_user_registration GET /users/cancel(.:format) {:action=>"cancel", :controller=>"registrations"} user_registration POST /users(.:format) {:action=>"create", :controller=>"registrations"} new_user_registration GET /users/register(.:format) {:action=>"new", :controller=>"registrations"} edit_user_registration GET /users/edit(.:format) {:action=>"edit", :controller=>"registrations"} PUT /users(.:format) {:action=>"update", :controller=>"registrations"} DELETE /users(.:format) {:action=>"destroy", :controller=>"registrations"} user_confirmation POST /users/confirmation(.:format) {:action=>"create", :controller=>"devise/confirmations"} new_user_confirmation GET /users/confirmation/new(.:format) {:action=>"new", :controller=>"devise/confirmations"} GET /users/confirmation(.:format) {:action=>"show", :controller=>"devise/confirmations"} users GET /users(.:format) {:action=>"index", :controller=>"users"} POST /users(.:format) {:action=>"create", :controller=>"users"} new_user GET /users/new(.:format) {:action=>"new", :controller=>"users"} edit_user GET /users/:id/edit(.:format) {:action=>"edit", :controller=>"users"} user GET /users/:id(.:format) {:action=>"show", :controller=>"users"} PUT /users/:id(.:format) {:action=>"update", :controller=>"users"} DELETE /users/:id(.:format) {:action=>"destroy", :controller=>"users"}note matching http verb Post /users and user_registrations path was matched first.
POST /users(.:format) {:action=>"create", :controller=>"users"} user_registration POST /users(.:format) {:action=>"create", :controller=>"registrations"}But don’t know what is the best way to avoid this conflict. Can you shed some light on this? I could not figure out from your tutorial.
Thanks in advance,
George
I post question on StackOverflow as well.
http://stackoverflow.com/questions/9644127/error-uninitialized-constant-registration-using-devise-for-user-self-registrat
@Tony : Thank you so much for those 2 tutorials. It was a great help for me to implement the base app of a future quality management tool. I needed a database RBAC and the Devise/Cancan combo works perfectly for me. Without your detailed instructions, I think I would still be trying to make it all work together.
Many thanks again.
Laetitia
Where is the code for this method error_messages() in the edit.html.erb view?
Regarding the use of error_messages() handler in these views, I see that in Rails 3+, this been removed as a base Rails helper, and they just wany you to handle it right in the view with your own html output.
Here is a link with the reasons it was removed, and sample code you can use in yours views instead: http://www.suffix.be/blog/error-messages-for-rails3
Also, here is a link where someone has written a replaced helper so you can still make a help call in your views: http://www.emersonlackey.com/article/rails3-error-messages-for-replacement
Regarding the habtm_checkboxes() helper in the partial view _user_fields.html.erb, here is the code you can use to create that helper method:
https://github.com/jtrupiano/habtm_checkboxes/blob/master/lib/habtm_checkboxes.rb
Just add this to you addp/helper directory as a separate file, or add it to the existing /app/helpers/application_helper.rb file.
Thanks for the insight, Matt. I’ve been super busy
Thanks Tony, your post really shed a lot of insight on how Devise and CanCan work together.
Would you be willing to share how you test your controllers though? Perhaps how you would set it up with fixtures like FactoryGirl (or whatever you use)? I understand you’re very busy, but it would be so awesome to see how you do it. Thanks again!
Im curious about how the object is used in cancan. In your example you mixed up the object. Is there a difference between can? :read, User vs can? :read, u? It seems that User would be “can this person read users” and u would be “can this user read this user”.
In the code below, from the example above… Why didnt you user u as the object for :read and :edit, but you did for :delete?
Thanks
Adam
“Are you sure?”, :method => :delete) {} %>
Edit
resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
(leave blank if you don’t want to change it)
“off” %>
(we need your current password to confirm your changes)
Cancel my account
Unhappy? { :confirm => “Are you sure?” }, :method => :delete %>.
I am having this code but it’s not display role check box tag an sign up page….
3 Trackbacks
[...] A part 2 of this post is now available No TweetBacks yet. (Be the first to Tweet this post) Share and Enjoy: [...]
[...] http://www.tonyamoyal.com/2010/09/29/rails-authentication-with-devise-and-cancan-part-2-restful-reso... [...]
[...] devise + cancanhttp://www.tonyamoyal.com/2010/09/29/rails-authentication-with-devise-and-cancan-part-2-restful-reso… [...]