CanCan Separate Role Model
I've been following this guide on 开发者_运维百科the Separate Role Model implementation in CanCan. When a User
, tries to sign up this error is thrown when creating the Assignment
.
User(#21477600) expected, got Symbol(#5785720)
I'm using a Devise generated User
with the following before_save
functions
class User < ActiveRecord::Base
.
.
.
def create_profile
profile = Profile.new :user_id => :id
end
def create_role
Assignment.new :user => :id, :role => Role.find_by_role("user").id
end
end
I want to default the user's role to "user", but I'm obviously doing something wrong. How should this be implemented?
Not sure if you've seen this or not, but Ryan Bates has produced a wonderful document regarding:
Separate Role Models
EDIT:
Here's what I am currently using. I believe your 'Assignment' is the same as my 'UserRole'.
user.rb
#--
# Relationship
has_many :user_roles, :dependent => :destroy, :uniq => true
has_many :roles, :through => :user_roles, :uniq => true
#--
# Instance Method
# Determine if the user has a specified role
# You can find this method at: https://github.com/ryanb/cancan/wiki/Separate-Role-Model
# Written by Ryan Bates, I added the downcase though to detect 'Admin' vs 'admin'.
# Example:
# user.has_role? :Admin
# => true
def has_role?(role_sym)
roles.any? { |role| role.name.underscore.to_sym == role_sym.downcase }
end
role.rb
# id :integer(4) not null, primary key
# name :string(255)
#--
# Relationship
has_many :user_roles, :dependent => :destroy, :uniq => true
has_many :users, :through => :user_roles, :uniq => true
user_role.rb
# id :integer(4) not null, primary key
# user_id :integer(4)
# role_id :integer(4)
#--
# Relationship
belongs_to :user
belongs_to :role
Then in my ability.rb
def initialize(user)
user ||= User.new # in case of a guest
if user.has_role? :Admin # The user is an Administrator
can :manage, :all
else
can :read, :all
end
end
Then I can easily assign roles, like in my seed file by doing something like:
# Create Users
...
# Roles
admin = Role.create!(:name => "admin")
standard = Role.create!(:name => "standard")
# UserRoles :Admin
user1.roles << admin
user2.roles << standard
So by calling user.roles << [role_name], I am essentially creating a UserRole, which has a user_id and a role_id.
There might be some more effective ways to accomplish this, but I cant tell without the exact model associations.
Anyway, I think this should work:
def create_role
Assignment.new :user => self, :role => Role.find_by_role("user")
end
Since you specify :user and not :user_id, you should pass self. The same thing for :role. If you had specified :role_id then you should have entered .id after find_by_role but since you only specify :role then remove .id
It looks like you're passing symbols to hash conditions that are expecting objects.
DanneManne's answer should work. You could alternatively do
Assignment.new( :user_id=>self.id, :role_id => Role.find_by_role('user').id )
(but Danne's is better, imo)
One last suggestion -- why not say the name of the role is "name", not "role". So then you'd be doing, Role.find_by_name('user'). That would be easier for a subsequent programmer to follow.
Firstly, you should not use save callback because it will be fired on both create & update.
Secondly, if you set up associations between models like that:
class User < ActiveRecord::Base
has_one :profile
has_many :assignments
end
class Profile < ActiveRecord::Base
belongs_to :user
end
class Assignment < ActiveRecord::Base
belongs_to :user
belongs_to :role
end
You will have convenient methods like user.profile
, user.build_profile
and user.create_profile
. Build & create will set up user_id
on profile automatically. You can use them in your callbacks without having to define any methods.
Note that before user is saved it does not have an id. So you need to use either before_create :build_profile
either after_create :create_profile
. The first one will create profile in memory that will be autosaved after user is saved, the second one is pretty straightforward.
There will be similar methods for assignments too: user.assignments.build
user.assignments.create
. So the final code for User will look something like this
class User < ActiveRecord::Base
has_one :profile
has_many :assignments
after_create :create_profile, :create_assignment
def create_assignment
assignments.create :role => Role.find_by_role("user")
end
end
精彩评论