I just added rolify to my Rails 6 app, which is using UUID for all tables.
After the initial errors I found that I need to change my migrations slightly to deal with the UUID. I am also using a model named 'Person' instead of the default 'User'. I have tried restarting my server (several times) but I still get the following:
2.6.2 :002 > p.add_role :admin
Traceback (most recent call last): 1: from (irb):2 NoMethodError (undefined method `add_role' for #Person::ActiveRecord_Relation:0x00007fe37ec29408>) 2.6.2 :003 >
Here are the applicable models:
role.rb
class Role < ApplicationRecord
has_and_belongs_to_many :people, :join_table => :people_roles
belongs_to :resource,
:polymorphic => true,
:optional => true
validates :resource_type,
:inclusion => { :in => Rolify.resource_types },
:allow_nil => true
scopify
end
person.rb
class Person < ApplicationRecord
rolify
def full_name
"#{self.last_name}, #{self.first_name}"
end
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :trackable
end
applicable schema:
create_table "people", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "last_name"
t.string "first_name"
t.string "gender"
t.uuid "personable_id"
t.string "personable_type"
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.integer "failed_attempts", default: 10, null: false
t.string "unlock_token"
t.datetime "locked_at"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.string "type"
t.index ["email"], name: "index_people_on_email", unique: true
t.index ["reset_password_token"], name: "index_people_on_reset_password_token", unique: true
t.index ["unlock_token"], name: "index_people_on_unlock_token", unique: true
end
create_table "people_roles", id: false, force: :cascade do |t|
t.uuid "person_id"
t.uuid "role_id"
t.index ["person_id", "role_id"], name: "index_people_roles_on_person_id_and_role_id"
end
create_table "roles", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "name"
t.uuid "resource_id"
t.string "resource_type"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["name", "resource_type", "resource_id"], name: "index_roles_on_name_and_resource_type_and_resource_id"
t.index ["name"], name: "index_roles_on_name"
end
Any help on this would be much appreciated!
Your problem is that p
is not a Person
but a collection of Person
objects as indicated by ActiveRecord_Relation
.
If you would like all of those people to have the admin role then it would be
p.each {|person| person.add_role :admin }
Otherwise you will need to change the code that populates p
to return a single Person
object. For instance if you are populating p
as
p = Person.where(uuid: some_uuid)
Then change this to
p = Person.find_by(uuid: some_uuid) # or Person.find_by_uuid(some_uuid)
find_by
will return an instance of the class (or nil
none are found) by selecting the first record where the condition is true. Since uuid
by virtue is unique this should not be an issue.