I have implemented Devise and Cancan using documentation for a simple Todo Rails App. I was able to hide the content Edit feature for user without admin role.
However, I cannot verify that user with admin roll can access the Edit feature because I cannot add/edit user roles.
How do you allow users to choose role via New and Edit Registration (Devise) views???
Here is the assignment I am trying to complete:
Add Multiple Roles to Your User to Create an ‘Admin’
Goal: To set up multiple roles for each User in order to create an admin.
Steps:
a. Add a an attribute of name(string) to your ‘user’ model.
b. Create a new model called ‘role’ and create an additional model called ‘user_role.’ The ‘user_role’ model will be the join table. Use a many to many association so that a user has many user_roles, and a role has many user_roles. The user_role model should belong to both user and role.
c. Add a an attribute of admin(string) to your ‘role’ model.
d. Create a method to check if a user is an admin in your ‘user’ model. You can use a method called ‘def admin?’
Here is my code:
app/models/user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :todos
has_many :roles, :through => :user_roles
before_create :setup_default_role_for_new_users
ROLES = %w[admin user]
def role_symbols
[role.to_sym]
end
def roles=(roles)
self.roles_mask = (roles & ROLES).map { |r| 2**ROLES.index(r) }.inject(0, :+)
end
def roles
ROLES.reject do |r|
((roles_mask.to_i || 0) & 2**ROLES.index(r)).zero?
end
end
def role?(role)
roles.include?(role.to_s)
end
def setup_default_role_for_new_users
if self.role.blank?
self.role = "user"
end
end
end
app/models/todo.rb
class Todo < ActiveRecord::Base
belongs_to :user
end
app/db/migrate/20140827183230_add_role_to_users.rb
class AddRoleToUsers < ActiveRecord::Migration
def change
add_column :users, :role, :string
end
def self.up
add_column :users, :role, :string
end
def self.down
remove_column :users, :role
end
end
views/devise/registrations/new.html.erb
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %></div>
<div><%= f.label :password %> <% if @validatable %><i>(<%= @minimum_password_length %> characters minimum)</i><% end %><br />
<%= f.password_field :password, autocomplete: "off" %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %></div>
<% for role in Role.all %>
<div>
<%= check_box_tag "user[role_ids][]", role.id, @user.roles.include?(role) %>
<%=h role.name %>
</div>
<% end %>
<%= hidden_field_tag "user[role_ids][]", "" %>
<div><%= f.submit "Sign up" %></div>
<% end %>
<%= render "devise/shared/links" %>
views/devise/registrations/new.html.erb
<h2>Edit <%= resource_name.to_s.humanize %></h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %></div>
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
<div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
<% end %>
<div><%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
<%= f.password_field :password, autocomplete: "off" %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %></div>
<div><%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password, autocomplete: "off" %></div>
<% for role in Role.all %>
<div>
<%= check_box_tag "user[role_ids][]", role.id, @user.roles.include?(role) %>
<%=h role.name %>
</div>
<% end %>
<%= hidden_field_tag "user[role_ids][]", "" %>
<div><%= f.submit "Update" %></div>
<% end %>
<h3>Cancel my account</h3>
<p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %></p>
<%= link_to "Back", :back %>
Please let me know if more code samples are required to assist in answering this question.