A FactoryGirl factory for a many-to-many self-referenced model - ruby-on-rails-3

I've got a self-referenced user model:
class User < ActiveRecord::Base
has_and_belongs_to_many :following,
:class_name => "User",
:foreign_key => 'follower_id',
:association_foreign_key => 'following_id',
:join_table => 'followers'
has_and_belongs_to_many :followers,
:class_name => "User",
:foreign_key => 'following_id',
:association_foreign_key => 'follower_id',
:join_table => 'followers'
...
end
I'm having trouble coming up with a FactoryGirl factory to test this behavior. I've tried some variations of
factory :user do
...
factory :following do
after(:create) do |user, evaluator|
create_list(:user, evaluator.followers_count, follower: user)
end
end
end
but had no luck with it. How would you do this? Thanks for any input.

Related

Friendship has_many through model with multiple status'

Currently my User model has the following code:
has_many :notifications
has_many :friendships
has_many :friends, :through => :friendships, :conditions => { status: 'accepted' }
has_many :requested_friends, :through => :friendships, :source => :friend, :conditions => { status: 'requested' }
has_many :pending_friends, :through => :friendships, :source => :friend, :conditions => { status: 'pending' }
And my Friendship model is as follows:
belongs_to :user
belongs_to :friend, :class_name => "User"
def self.request(user, friend)
unless user == friend or Friendship.exists?(user_id: user, friend_id: friend)
transaction do
Friendship.create(:user => user, :friend => friend, :status => 'pending')
Friendship.create(:user => friend, :friend => user, :status => 'requested')
end
else
return "failed"
end
end
def self.accept(user, friend)
unless user == friend or Friendship.exists?(user_id: user, friend_id: friend)
transaction do
accepted_at = Time.now
accept_one_side(user, friend, accepted_at)
accept_one_side(friend, user, accepted_at)
end
else
return "failed"
end
end
def self.accept_one_side(user, friend, accepted_at)
request = find_by_user_id_and_friend_id(user, friend)
request.status = 'accepted'
request.accepted_at = accepted_at
request.save!
end
When I try to run my cucumber tests, however, I am getting this error:
SQLite3::SQLException: no such column: users.status: SELECT "users".* FROM "users" INNER JOIN "friendships" ON "users"."id" = "friendships"."friend_id" WHERE "users"."status" = 'accepted' AND "friendships"."user_id" = ? (ActionView::Template::Error)
I think that this means it is trying to only include inside in for example pending_friends, users which have the attribute status = "pending", where it should actually be including users who belong to friendships which have attribute status = "pending"
Is this right? How would I go about fixing this?
I can't find any documentation on use of :conditions with has_many, but based on the error that's being generated, I gather that the conditions specified are assumed to apply to the model that is subject of the has_many, not the target model or the model through which it is being reference.
I have updated to the following and this works:
has_many :notifications
has_many :friendships
has_many :accepted_friendships, :class_name => "Friendship", :conditions => {status: 'accepted'}
has_many :requested_friendships, :class_name => "Friendship", :conditions => {status: 'requested'}
has_many :pending_friendships, :class_name => "Friendship", :conditions => {status: 'pending'}
has_many :friends, :through => :accepted_friendships
has_many :requested_friends, :through => :requested_friendships, :source => :friend
has_many :pending_friends, :through => :pending_friendships, :source => :friend
If anyone has a different approach without having to create accepted_friendshis, requested_friendships, and pending_friendships, however, I would love to hear it!
status is a column for friendships table.
So when you are writing the code, then mention the table name or else it will take the table of the Current model.
has_many :friends, -> { where "friendships.status = 'accepted'" }, :through => :friendships

Rails has_many self referential

I have an accounts model as follows (simplified):
class Account < ActiveRecord::Base
attr_accessible :account_number, :display_name, :master_account_id
has_many :child_accounts, :class_name => "Account", :foreign_key => "id"
belongs_to :master_account, :class_name => "Account", :foreign_key => "master_account_id"
end
#account.master_account is currently working correctly, but I also want to be able to access #account.child_accounts - what do I need to do in order to fix that?
I think it has to be the other way round:
class Account < ActiveRecord::Base
has_many :child_accounts, :class_name => "Account", :foreign_key => "master_account_id"
belongs_to :master_account, :class_name => "Account"
end

RSpec testing non validating has_many through relationship

I have a many to many relationship between documents.
Say I have document1 and document2. I have a many to many table where there are parents and children.
document.rb
has_many :child_relationships, :class_name => "DocumentRelationship", :foreign_key => "child_id", :dependent => :destroy
has_many :parents, :through => :child_relationships, :source => :parent
has_many :parent_relationships, :class_name => "DocumentRelationship", :foreign_key => "parent_id", :dependent => :destroy
has_many :children, :through => :parent_relationships, :source => :child
document_relationship.rb
belongs_to :parent, :class_name => "Document", :foreign_key => "parent_id"
belongs_to :child, :class_name => "Document", :foreign_key => "child_id"
validates_uniqueness_of :child_id, :scope => [:parent_id]
validates_presence_of :parent_id
validates_presence_of :child_id
validate :obeys_chronology
def obeys_chronology
errors.add(:child_id, "must be created after its parent") if child_id.to_i < parent_id.to_i
errors.add(:child_id, "cannot be its own parent") if child_id.to_i == parent_id.to_i
end
If I say document2.children << document1 it appropriately fails validation, but I don't know how to write a test for this.
Is there a better way to do this?
Add it to the collection
document2.children << document1
document2.children.contain?(document1).should == false
Then make sure it's not in there.

Rails has_many find associated items

So, got 2 models:
class Match < ActiveRecord::Base
has_many :rounds
has_many :participations
has_many :players, :through => :participations
belongs_to :clan_1, :class_name => "Clan", :foreign_key => "clan_1_id"
belongs_to :clan_2, :class_name => "Clan", :foreign_key => "clan_2_id"
belongs_to :winner, :class_name => "Clan", :foreign_key => "winner_id"
belongs_to :league
belongs_to :tournament
validates :clan_1_id, :presence => true
validates :clan_2_id, :presence => true
scope :by_league, lambda { |league| where("league_id == ?",league.id) }
scope :by_tournament, lambda { |tournament| where("tournament_id == ?",tournament.id) }
scope :played, where("played is not NULL")
scope :not_played, where("played is NULL")
end
class Clan < ActiveRecord::Base
has_many :players
has_many :rounds_won, :class_name => "Round", :foreign_key => "winner_id"
has_many :rounds_blue, :class_name => "Round", :foreign_key => "clan_blue_id"
has_many :rounds_purple, :class_name => "Round", :foreign_key => "clan_purple_id"
has_many :matches_won, :class_name => "Match", :foreign_key => "winner_id"
has_and_belongs_to_many :leagues
has_and_belongs_to_many :tournaments
def matches
Match.where("clan_1_id = ? OR clan_2_id = ?",self.id, self.id)
end
def matches_lost
matches.where("winner_id != ?", self.id)
end
def matches_drawn
matches.played.where("winner_id is NULL")
end
end
and I want to fetch all clans, which taken part in match.
You're over thinking it. Rails makes it very easy for you to do this.
class Comment < ActiveRecord::Base
belongs_to :post
end
class Post < ActiveRecord::Base
has_many :comments
end
#post.comments
If you have a column in your comment table with "modelName"_id (eg post_id) rails with automatically hook up the foreign key.
All you have to do is call #model1.model2 assuming #model1 is an instance of the model1 object.
If you want to hook up the query yourself you could use the where() method.
#comments = Comment.where(:post_id => some_id)
If you alter your associations a little bit you can utilize includes() in scopes:
class Match < ActiveRecord::Base
belongs_to :clan_1, :class_name => "Clan"
belongs_to :clan_2, :class_name => "Clan"
end
class Clan < ActiveRecord::Base
has_many :one_matches, :class_name => 'Match', :foreign_key => :clan_1_id
has_many :two_matches, :class_name => 'Match', :foreign_key => :clan_2_id
end
Then you can add this scope:
class Clan < ActiveRecord::Base
scope :participated_in_match, includes(:one_matches, :two_matches).where("matches.id IS NOT NULL")
end
This isn't tested so please let me know if you get unexpected results.
Quite simply:
model_two_object = Model_2.first # For clarity only, change to suit your needs
model_two_object.models_1

How to write an AREL statement on a self-referencing table

I have written quite a few AREL statements, but I'm tying myself in knots over this one. Here is my situation:
class Product < AR::Base
has_many :parents, :class_name => "ProductLink", :foreign_key => :to_product_id
has_many :children, :class_name => "ProductLink", :foreign_key => :from_product_id
# has an attribute called "identifier"
end
class ProductLink < AR::Base
belongs_to :parent, :class_name => "Product", :foreign_key => :from_product_id
belongs_to :child, :class_name => "Product", :foreign_key => :to_product_id
end
I want to retrieve all of the Products that have a child product with an identifier that matches some value.
I have twisted myself into a pretzel with this, seems easy, but I have been looking at it for too long now. I appreciate any help!
Got it!
brand.products.joins(:children => :child).where(:children => { :child => { :searchable_identifier.matches => "2136" } } )
That works great. See the hashed joins? That's what was throwing me off.