I am trying to set up a basic user authentication - I have the user login stuff sorted and am up to adding roles for the user.
Essentially I want my Users to have many Roles, which gives them access to Rights.
I wrote some seed data but keep getting the error:
rake aborted!
undefined method `roles' for #<Array:0x007f8c0581ba80>
My seed data looks like:
#user
user = User.create!([{ email: 'admin#admin.com', first_name: 'Admin', last_name: 'Test', password: 'admin', password_confirmation: 'admin'}])
user.roles << admins = Role.create!(:name => "Admin")
#user roles
create = Right.create!(:resource => "users", :operation => "CREATE")
read = Right.create!(:resource => "users", :operation => "READ")
update = Right.create!(:resource => "users", :operation => "UPDATE")
delete = Right.create!(:resource => "users", :operation => "DELETE")
#add the roles to the admin
admins.rights << read
admins.rights << create
admins.rights << update
admins.rights << delete
rake db:migrate works fine and all table columns are as I expect them to. Just when I run rake db:seed it aborts with the above error. I understand what the error is saying - I just can't see where I am not defining the has_many to roles.
I have gone through the models very closely but can't seem to find what I have missed.
and my model files looks like this:
class User < ActiveRecord::Base
has_secure_password
has_many :assignments
has_many :roles, :through => :assignments
attr_accessible :email, :first_name, :last_name, :password, :password_confirmation
validates_presence_of :email, :on => :create
validates :password, :confirmation => true
validates :password_confirmation, :presence => true
validates_uniqueness_of :email
#will be using this later to check if the user has access to resources/actions
# def can?(action, resource)
# roles.includes(:rights).for(action, resource).any?
# end
end
class Role < ActiveRecord::Base
has_many :grants
has_many :assignments
has_many :users, :through => :assignments
has_many :rights, :through => :grants
scope :for, lambda{|action, resource|
where("rights.operation = ? AND rights.resource = ?",
Right::OPERATION_MAPPINGS[action], resource
)
}
end
class Right < ActiveRecord::Base
attr_accessible :operation, :resource
has_many :grants
has_many :roles, :through => :grants
OPERATION_MAPPINGS = {
"new" => "CREATE",
"create" => "CREATE",
"edit" => "UPDATE",
"update" => "UPDATE",
"destroy" => "DELETE",
"show" => "READ",
"index" => "READ"
}
end
class Grant < ActiveRecord::Base
attr_accessible :right_id, :role_id
belongs_to :role
belongs_to :right
end
class Assignment < ActiveRecord::Base
belongs_to :user
belongs_to :role
attr_accessible :role_id, :user_id
end
any help would be greatly appreciated.
You should not create one user as array of users. Try delete the square brackets in User.create!()
user = User.create!({email: 'admin#admin.com', first_name: 'Admin', last_name: 'Test', password: 'admin', password_confirmation: 'admin'})
Just ditch the []'s and {}'s in the first line, eg:
user = User.create!(email: 'admin#admin.com', first_name: 'Admin', last_name: 'Test', password: 'admin', password_confirmation: 'admin')
Related
I'm trying to run RSpec against a working large codebase (I'm relatively new to Rails), but it fails on this point; My bet that it has something to do with the FactoryGirl definitions.
Overview of the model :
class User < ActiveRecord::Base
# ...
has_many :friends, :conditions => {:approved => true}
has_many :friendships, :class_name => "User", :source => :friend, :through => :friends
# ...
The method to test :
# models/user.rb
def add_friend(user_id, friend_id)
#friendship = self.friends.new({:user_id => user_id, :friend_id => friend_id})
return false unless #friendship.save
end
The FactoryGirl factories :
FactoryGirl.define do
factory :user, :class => User do |f|
# ...
end
factory :friend, :class => Friend do |f|
f.user_id { Faker::Base.regexify(/\d{1,3}/)}
f.friend_id { Faker::Base.regexify(/\d{1,3}/)}
# ...
end
end
The Spec :
# specs/models/user_spec.rb
it "Adds friends" do
#current_user.add_friend(#current_user.id, #friend_1.id).should be_valid
end
The Error :
ActiveRecord::StatementInvalid: Could not find table 'friends'
Any feedback is highly welcome, Thanks.
You may need to run rake db:migrate RAILS_ENV=test.
I want to get a list of say moderators from User model, it works in this case
u = User.new(:name => "n", :surname => "s", :email => "a#m.c", :password => "x")
u.add_role(:moderator)
u.save!
but if i assign a resource to User model like this, it's not listing users with role moderator
u = User.new(:name => "m", :surname => "b", :email => "a#m.c", :password => "x")
u.add_role(:moderator, Post.first)
u.save!
UPDATE
post.rb
class Post < ActiveRecord::Base
attr_accessible :user_id, :content
belongs_to :user
end
user.rb
class User < ActiveRecord::Base
rolify
end
You should add resourcify to your Post model and other all models you want to apply roles on, as described in readme.
So, your Post model should look like:
Post.rb
class Post < ActiveRecord::Base
resourcify
attr_accessible :user_id, :content
belongs_to :user
end
* EDIT *
You can get all users with role :admin using User.with_all_roles({:name => :admin})
I've created a vanilla project uses rolify with same User and Post model. Change Post model and add resourcify, change User model and add has_many :posts
user = User.create(...)
user.add_role :admin
post = user.posts.create(...)
user2 = User.create(...)
user2.add_role(:moderator, post)
Seems working with these:
User.with_all_roles({:name => :admin})
User.with_all_roles({:name => :moderator, :resource => Post })
User.with_all_roles({:name => :moderator, :resource => Post.first })
I'm folowwing Charles Max Wood tutorial on the twitter clone , flitter.
I'm having and error undefined method friendships when I launch rake db:seed .I'am trying to add friend via the rake db:seed task , The method add_friend is define in the User model. But i need help to define the method friendships so that the task can work .Thank you a lot for your help .
Here is the db/seeds.rb file
require 'faker'
require 'populator'
User.destroy_all
10.times do
user = User.new
user.username = Faker::Internet.user_name
user.email = Faker::Internet.email
user.password = "test"
user.password_confirmation = "test"
user.save
end
User.all.each do |user|
Flit.populate(5..10) do |flit|
flit.user_id = user.id
flit.message = Faker::Lorem.sentence
end
3.times do
User.add_friend(User.all[rand(User.count)])
end
end
and there is the user file.
class User < ActiveRecord::Base
# new columns need to be added here to be writable through mass assignment
attr_accessible :username, :email, :password, :password_confirmation
attr_accessor :password
before_save :prepare_password
validates_presence_of :username
validates_uniqueness_of :username, :email, :allow_blank => true
validates_format_of :username, :with => /^[-\w\._#]+$/i, :allow_blank => true, :message => "should only contain letters, numbers, or .-_#"
validates_format_of :email, :with => /^[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
validates_presence_of :password, :on => :create
validates_confirmation_of :password
validates_length_of :password, :minimum => 4, :allow_blank => true
has_many :flits, :dependent => :destroy
has_many :friendships
has_many :friends, :through => :friendships
def self.add_friend(friend)
friendship = friendships.build(:friend_id => friend.id)
if !friendship.save
logger.debug "User '#{friend.email}' already exists in the user's friendship list."
end
end
# login can be either username or email address
def self.authenticate(login, pass)
user = find_by_username(login) || find_by_email(login)
return user if user && user.password_hash == user.encrypt_password(pass)
end
def encrypt_password(pass)
BCrypt::Engine.hash_secret(pass, password_salt)
end
private
def prepare_password
unless password.blank?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = encrypt_password(password)
end
end
end
friendship.rb
class Friendship < ActiveRecord::Base
attr_accessible :friend_id, :user_id
belongs_to :user
belongs_to :friend, :class_name => 'User'
validates_uniqueness_of :friend_id, :scope => :user_id
validates_presence_of :user_id, :friend_id
end
I think what you want to be doing is calling add_friend on the instance user, and not on the class User:
3.times do
user.add_friend(User.all[rand(User.count)])
end
Also your add_friend method should be an instance method, not a class method, so you don't need the self:
def add_friend(friend)
friendship = friendships.build(:friend_id => friend.id)
if !friendship.save
logger.debug "User '#{friend.email}' already exists in the user's friendship list."
end
end
You should define this method as a class method not instance method:
def self.add_friend(friend)
friendship = friendships.build(:friend_id => friend.id)
if !friendship.save
logger.debug "User '#{friend.email}' already exists in the user's friendship list."
end
end
After reading about attr_accessible in the Rails 3.1 API, I see that there is an as :admin option in there. I would like to know two things.
If the user has an admin flag, how do does my controller tell my model that the user is an admin.
If the user is an owner, can i specify :as => owner in my model, and once again how does my controller inform my model they are the owner of an item.
There is no built-in integration with models; you pass in the role in the assign_attributes call:
#project.assign_attributes(params[:project], :as => :admin)
The :as parameter defaults to :default, and you can pass in any symbol that you want. To integrate this into your User model, you could give it an attribute called role, and then do something like:
#project.assign_attributes(params[:project], :as => current_user.role.to_sym)
You can also bypass the protection using :without_protection:
#project.assign_attributes(params[:project], :without_protection => true)
In a similar way, new, create, create!, update_attributes, and update_attributes! methods all respect mass-assignment security. The Ruby on Rails guide on security has more info.
For both scenarios, you'd pass it in the same way that you declare it originally. So for example:
class User < ActiveRecord::Base
attr_accessible :name
attr_accessible :credit_card, :as => :admin
end
If you did
user = User.new(:name => "John", :credit_card => "1234123412341234")
Then you won't be able to assign the credit_card:
user.attributes # {:name => "John", :credit_card => nil}
However, if you state that it will be :as => :admin then it allows it
user = User.new({:name => "John", :credit_card => "1234123412341234"}, :as => :admin)
user.attributes # {:name => "John", :credit_card => "1234123412341234"}
More information:
http://www.enlightsolutions.com/articles/whats-new-in-edge-scoped-mass-assignment-in-rails-3-1
all the attributes you want to access as a specific user should be defined properly. For example:
class User < ActiveRecord::Base
attr_accessible :name
attr_accessible :credit_card, :as => :admin
end
This showed error for me.
But when i modied it to
class User < ActiveRecord::Base
attr_accessible :name
attr_accessible :name, :credit_card, :as => :admin
end
This worked fine when i used
#user.update_attributes(params[:user], :as => :admin)
I have a simple one-to-many relationship between user and micropost as below. I tried to add a new column called stage to the Micropost model. when I try to build a new Micropost and save, the stage column is always automatically set to nil. I have tried create, build - doesn't matter, the stage field is always set to nil. I am baffled, please help!
$ rails console
Loading development environment (Rails 3.0.5)
>> User.first.microposts.create!( :stage => "p", :content => "test 6" )
=> #<Micropost id: 2, content: "test 6", stage: nil, user_id: 1, created_at: "2011-04-23 22:14:20", updated_at: "2011-04-23 22:14:20">
...
class Micropost < ActiveRecord::Base
attr_accessible :content, :stage
attr_accessor :stage
belongs_to :user
validates :content, :presence => true, :length => { :maximum => 140 }
validates :user_id, :presence => true
default_scope :order => 'microposts.created_at DESC'
scope :from_users_followed_by, lambda { |user| followed_by(user) }
private
def self.followed_by(user)
followed_ids = %( SELECT followed_id FROM relationships
WHERE follower_id = :user_id)
where "user_id IN (#{followed_ids}) OR user_id = :user_id",
{ :user_id => user }
end
end
...
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email, :password, :password_confirmation
has_many :microposts, :dependent => :destroy
end
You need to remove line:
attr_accessor :stage
Without it everything works fine. I think it's conflict between attr_accessor and attr_accessible.