I am trying to learn how to use rolify with my Rails 5 app.
I've previously asked lots of questions about Rolify, most recently here but I've not been able to find help.
Im struggling to try to figure out how to remove an assigned role from a user.
The tables in my schema have tables for app_roles (which is my CRUD for making the roles that can separately be assigned to users), users, roles and user_roles - as:
create_table "app_roles", force: :cascade do |t|
t.string "title"
t.string "display_name"
t.integer "category"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.integer "failed_attempts", default: 0, null: false
t.string "unlock_token"
t.datetime "locked_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "organisation_id"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
t.index ["email"], name: "index_users_on_email", unique: true, using: :btree
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true, using: :btree
end
create_table "users_roles", id: false, force: :cascade do |t|
t.integer "user_id"
t.integer "role_id"
t.index ["user_id", "role_id"], name: "index_users_roles_on_user_id_and_role_id", using: :btree
end
create_table "roles", force: :cascade do |t|
t.string "name"
t.string "resource_type"
t.integer "resource_id"
t.datetime "created_at"
t.datetime "updated_at"
t.index ["name", "resource_type", "resource_id"], name: "index_roles_on_name_and_resource_type_and_resource_id", using: :btree
t.index ["name"], name: "index_roles_on_name", using: :btree
end
The associations are:
User
rolify strict: true # strict means you get true only on a role that you manually add
attr_accessor :current_role
Role
has_and_belongs_to_many :users, :join_table => :users_roles
belongs_to :resource,
:polymorphic => true,
:optional => true
App Roles (has no associations - It's the CRUD I use to make new roles via a form)
My user model has:
class User < ApplicationRecord
rolify strict: true # strict means you get true only on a role that you manually add
attr_accessor :current_role
I couldn't figure out how to assign roles just using rolify the way its shown in the wiki. That's why I made a separate resource. It's called assign roles. I have an assign_roles_controller.rb which has:
class Users::AssignRolesController < ApplicationController
before_action :authenticate_user!
def index
@app_roles = AppRole.all
end
def create
user = User.find(params[:users])
role = AppRole.find(params[:roles])
organisation = Organisation.find(current_user.organisation)
# byebug
user.add_role role.display_name, organisation
flash[:notice] = "Successfully created"
redirect_to action: :index
end
def show
@users = User.all
end
def update
end
def destroy
# user = User.find(params[:users])
# user = User.find(params[:id])
user = User.find_by_id(params[:id])
# User.find(params[:id])
# role = AppRole.find(params[:roles])
role = params[:user][:roles]
# assigned_role = user.roles
# user_roles = user.roles
# organisation = Organisation.first
organisation = Organisation.find(current_user.organisation)
# byebug
user.remove_role role.display_name, organisation
flash[:notice] = "Successfully created"
redirect_to root_path
end
end
The routes are nested, so I have:
resources :users, shallow: true do
scope module: :users do
resources :assign_roles
In my users index, I'm trying to show a list of users, with each of their roles and a remove role link. In the users/index.html.erb, I have:
<% user.roles.each do |role| %>
<table class="table table-bordered">
<tr>
<td><%= role.name.titleize %></td>
<td><%= link_to 'Remove role', assign_role_path(user.roles), method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
</table>
<% end %>
In the console, I can do:
Organisation.find_roles(nil, User.find_by(organisation_id:1))
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."organisation_id" = $1 LIMIT $2 [["organisation_id", 1], ["LIMIT", 1]]
Role Load (0.7ms) SELECT "roles".* FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = $1 AND (resource_type IN ('Organisation')) [["user_id", 43]]
=> #<ActiveRecord::AssociationRelation [#<Role id: 3, name: "asdf", resource_type: "Organisation", resource_id: 1, created_at: "2016-10-24 23:00:40", updated_at: "2016-10-24 23:00:40">, #<Role id: 4, name: "ffff", resource_type: "Organisation", resource_id: 1, created_at: "2016-11-09 00:26:56", updated_at: "2016-11-09 00:26:56">]>
The user has 2 roles.
When I try to remove one of them, I get an error that says:
undefined method `[]' for nil:NilClass
That error complains about this formulation (which is a copy from someone's suggestion on another SO post) in the destroy action of my assign roles controller:
role = params[:user][:roles]
When I try the destroy action as:
def destroy
user = User.find_by_id(params[:id])
assigned_role = user.roles
organisation = Organisation.find(current_user.organisation)
# byebug
user.remove_role assigned_role.display_name, organisation
flash[:notice] = "Successfully created"
redirect_to root_path
end
I then get an error which says:
undefined method `roles' for nil:NilClass
I think that error may have something to do with me trying to remove one role from the user but I'm using plural to find them all. I'm not sure how to find the specific one role that I'm referencing in the uses index.
Can anyone help me figure this out?
Please refer this once.
def destroy
user = User.find_by_id(params[:id])
assigned_role = user.role_name
organisation = Organisation.find(current_user.organisation)
# byebug
user.remove_role assigned_role, organisation
flash[:notice] = "Successfully created"
redirect_to root_path
end
I had a session on codementor and finally got to an answer.
The answer is:
def destroy
#/assign_roles/9?user_id=27
user = User.find_by_id(params[:user_id])
assigned_role = Role.find(params[:id])
organisation = Organisation.find(current_user.organisation)
user.remove_role assigned_role.name.to_sym, current_user.organisation #user.organisation
flash[:notice] = "role deleted"
redirect_to root_path
end
And in the view:
<td><%= link_to 'Remove role', assign_role_path(role, user_id: user), method: :delete, data: { confirm: 'Are you sure?' } %></td>
The specific issue is that the role name needs to be converted to a string before it can be removed and the user find method is searching in the closest resource (which means it is looking for an id in the role model, not the user model. That means that id will fail to return the user id. I don't understand the search structure of this part of rails - so I have to take that as given (without understanding it), but very happy, that after years of trying to solve this problem, I can move on.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With