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.
Related
So I have a User model, and a Group model which has several users thanks to the GroupUserAssociation model. Here's how my relationships are defined:
class Group < ActiveRecord::Base
has_many :group_users, :class_name => 'GroupUserAssociation', :foreign_key => :group_id
has_many :group_admins, :class_name => 'GroupUserAssociation', :foreign_key => :group_id, :conditions => ['level = 1']
has_many :group_not_admins, :class_name => 'GroupUserAssociation', :foreign_key => :group_id, :conditions => ['level = 0']
has_many :users, :through => :group_users, :source => :user
has_many :admins, :through => :group_admins, :source => :user
has_many :not_admins, :through => :group_not_admins, :source => :user
end
If I want to add/remove users to group, there is an elegant way to write it (elegant because it doesn't involves the GroupUserAssociation object):
Group.first.users << User.first # Adds to group
Group.first.users.delete(User.first) # Removed from group
But if I do
Group.first.admins << User.first
Group.first.admins.delete(User.first)
it also deletes the association (hence has the same effect as the first lines).
Is there an elegant way (without handling the GroupUserAssociation object to promote/demote admin (= to update GroupUserAssociation.level from 1 to 0) ?
I could do
Group.first.users.delete(User.first) # Removed from group
Group.first.admins << User.first
But that would mean 2 times commiting to DB which is not really good...
I read there are some nice things for this in Rails 4, but unfortunately I'm using Rails 3.2...
Thanks
We do this using this code:
#config/routes.rb
resources :entries do
post :category
delete ":category_id", to: :category, as: "remove_category"
end
#Categories
def category
entry = #entry = Entry.find(params[:entry_id])
category = #category = Category.find(params[:category_id])
#Actions
entry.categories << category if request.post? && !entry.categories.include?(category)
entry.categories.delete(category) if request.delete?
#Return
respond_to do |format|
format.html { redirect_to collection_path }
format.js
end
end
Models:
class User < ActiveRecord:Base
has_many :roles
has_many :networks, :through => :roles
end
class Network < ActiveRecord:Base
has_many :roles
has_many :network, :through => :roles
end
class Role < ActiveRecord:Base
attr_accesible :user_id, :network_id, :position
belongs_to :user
belongs_to :network
end
The default for role is "member"
In the console I can type:
> #role = Role.find(1)
> #role.position
=> "member"
But in my Rspec tests, I use FactoryGirl to create a user, network, and role. And I have the test #role.should respond_to(:position) I have also tried just assigning it #role.position = "admin". And no matter what, I get an error like:
Failure/Error: #role.should respond_to(:position)
expected [#<Role id:1, user_id: 1, position: "member", created_at...updated_at...>] to respond to :position
Am I missing something very basic?
EDIT:
factories.rb
FactoryGirl.define do
factory :user do
name "Example User"
sequence(:email) {|n| "email#{n}#program.com"}
end
factory :network do
sequence(:name) {|n| "Example Network #{n}"}
location "Anywhere, USA"
description "Lorem Ipsum"
end
factory :role do
association :user
association :network
position "member"
end
end
network_controller_spec
...
before(:each) do
#user = test_sign_in(FactoryGirl.create(:user)
#network = FactoryGirl.create(:network)
#role = FactoryGirl.create(:role, :user_id => #user.id, :network_id = #network.id)
#I have also tried without using (_id) I have tried not setting the position in the factories as well.
end
it "should respond to position" do
get :show, :id => #network
# This may not be the best or even correct way to find this. But there should only be one, and this method works in the console.
#role = Role.where(:user_id => #user.id, :network_id => #network.id)
#role.should respond_to(:position)
end
Jesse is correct in his comment, hopefully he will come back and write it as an answer, in the meantime, the code should be:
#role = Role.where(:user_id => #user.id, :network_id => #network.id).first
or
#role = Role.find_by_user_id_and_network_id(#user.id, #network.id)
As an aside, it seems a little odd to be testing the role class in the network controller spec (unless this is just an exploratory test to work out why things aren't working as expected).
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')
I am using Ruby on Rails 3.0.7 and I would like to DRY (Don't Repeat Yourself) my scope methods.
In the model file I have:
class Articles::Category < ActiveRecord::Base
scope :article_related_to, lambda { |user| where('articles_categories_article_relationships.user_id = ?', user.id) }
scope :comment_related_to, lambda { |user| where('comments_articles_article_category_relationships.user_id = ?', user.id) }
has_many :comment_article_category_relationships
has_many :comments,
:class_name => 'Comments::Articles::ArticleCategoryRelationship',
:through => :comment_article_category_relationships,
:source => :comment
has_many :article_relationships
:class_name => 'Articles::Categories::ArticleRelationship',
has_many :articles,
:through => :article_relationships,
:source => :article
end
By using the above code I can do this:
#comment.article_categories.comment_related_to(#current_user)
#comment.article_categories.article_related_to(#current_user)
How can I "DRY" scopes methods in order to make possible for both :article_related_to and :comment_related_to to use something like the following
#comment.article_categories.related_to(#current_user)
# In order to pass the correct "context" 'article' or 'comment' I thought
# something like
#
# #comment.article_categories.related_to(#current_user, 'article')
# #comment.article_categories.related_to(#current_user, 'comment')
#
# but, maybe, there is a way to retrieve automatically that "context" so to
# write only one "DRYed" scope method.
?
The best I can offer is the following:
scope :related_to, lambda { |user, context|
tbl = context == :article ? :articles_categories_article_relationships
: :comments_articles_article_category_relationships
where("#{tbl}.user_id = ?", user.id)
}
That gives you the #comment.article_categories.related_to(#current_user, :article) like you suggested. But I'm in agreement with Max Williams. This obfuscates your code unnecessarily with no real gain.
If you are really eager to obfuscate your code further you can do this:
def self.method_missing(method, *args)
if method =~ /^(.*)_related_to$/
related_to(*args, $1)
else
super
end
end
def self.related_to(user, context)
through = reflections[context.to_s.pluralize.to_sym].options[:through]
tbl = reflections[through].options[:class_name].underscore.pluralize.gsub('/', '_')
where("#{tbl}.user_id = ?", user.id)
end
Please note that I believe your associations have a couple of typos. Probably should be:
has_many :comment_article_category_relationships,
:class_name => 'Comments::Articles::ArticleCategoryRelationship'
has_many :comments,
:through => :comment_article_category_relationships,
:source => :comment
has_many :article_relationships,
:class_name => 'Articles::Categories::ArticleRelationship'
has_many :articles,
:through => :article_relationships,
:source => :article
I'm at my wit's end trying to handle these errors. Basically, I've created the following User and Relationship patterns, using Mongoid to handle my database. This seems like a near-carbon copy of the example at the bottom of the page here. I'm trying to call any of the following:
user1.relationships.find(:all, :conditions => {:rel_user => user_in_question, :rel_type => "following" })
user1.relationships.all(:conditions => {:rel_user => user_in_question, :rel_type => "following" })
user1.relationships.where(:rel_type => "following")
user1.relationships.following #with a named scope
These all seem to just return the entire relationships array; they don't search through by criteria. The find() method also throws an error saying that it only can take 1 argument. The im_following? method always returns true.
I'm not sure if it's better to post code in-line or from gist, so here are the gists:
user.rb
user_follow_spec.rb
relationship.rb
I would appreciate any help.
Rockmanioff, I have also came across the same issue. You might want to look at this as well. Mongoid plans on supporting this feature on their release candidate version. For now, we have to do things manually.
class User
include Mongoid::Document
include Mongoid::Timestamps
references_many :fans, :stored_as => :array, :class_name => 'User', :inverse_of => :fan_of
references_many :fan_of, :stored_as => :array, :class_name => 'User', :inverse_of => :fans
def become_fan_of user
fan_of << user
self.save
user.fans << self
user.save
end
def is_a_fan? user
fan_of_ids.include? user.id
end
def unfan user
fan_of_ids.delete user.id
self.save
user.fan_ids.delete self.id
user.save
end
...
end
In console, you can do:
User.first.become_fan_of User.last
User.first.is_a_fan? User.last
User.first.unfan User.last
In your case you might want to substitute "fan / fan_of" for "followers / following respectively". Hope this helps.
I'd advise you to simplify your relationships by using self-referential associations. Check out my answer to this question:
How-to: User has fans
I think this is pretty close to the association you want:
class User
include Mongoid::Document
references_many :following,
:class_name => 'User',
:stored_as => :array,
:inverse_of => :followed_by
references_many :followed_by,
:class_name => 'User',
:stored_as => :array,
:inverse_of => :following
end
# let's say we have users: al, ed, sports_star, movie_star
sports_star.followed_by << al
movie_star.followed_by << al
sports_star.followed_by << ed
movie_star.followed_by << ed
movie_star.followed_by # => al, ed
al.following # => sports_star, movie_star
Try this:
class User
# follows and followers
references_many :follows, :stored_as => :array , :inverse_of => :followers ,:class_name=>"User"
references_many :followers, :stored_as => :array , :inverse_of => :follows ,:class_name=>"User"
def followers
followers.map
end
end