开发者

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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜