RSpec do I test both sides of a relationship - testing

lets say I have two models
class User < ActiveRecord::Base
has_many :friendships, :dependent => :destroy
has_many :followings, :through => :friendships, :foreign_key => "followed_id"
end
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :following, :class_name => "User", :foreign_key => "followed_id"
end
now in my user_spec.rb I have this test
it "should delete all friendships after user gets destroyed" do
#user.destroy
[#friendship].each do |friendship|
lambda do
Friendship.find(friendship)
end.should raise_error(ActiveRecord::RecordNotFound)
end
end
is this the right place to test the :dependent => :destroy relation or does this belong inside the friendship_spec.rb or doesn't it matter in which of the two specs I test this?

You might consider using shoulda_matchers to test your associations:
# user_spec.rb
it { should have_many(:friendships).dependent(:destroy) }
# friendship_spec.rb
it { should belong_to(:user) }
Having each model test its own associations is the best approach IMHO.

This sort of thing is sometimes a matter of taste, but I think the spec for User is probably the best place to test this. The method you're calling to start the test is a method on User, so it makes sense to test it along the other tests for User as well.

Related

How can I reference the same model twice and create the association in a multiple select form?

I have two classes (Game and Report) and want to link them with an additional attribute (default = yes or no).
The game should then have default_reports and optional_reports.
The association is then updated by selecting the default and optional reports in a select (multiple) in the games create/edit form.
I have tried using has_many and through as well as polymorphic associations, but nothing seems to fit the use case, where the associated objects are fixed and you only want to manage associations.
class Game < ActiveRecord::Base
has_many :game_reports
has_many :reports, :through => :game_reports
end
class Report < ActiveRecord::Base
has_many :game_reports
has_many :games, :through => :game_reports
end
class GameReport < ActiveRecord::Base
belongs_to :game
belongs_to :report
end
Any help is appreciated!
Thanks
this is just the model. the view and form to create the records is an entirely different matter.
you can always add a conditions option to has_many. I'm assuming you're going to add default to game_reports so change your class to something like.
class Game < ActiveRecord::Base
has_many :game_reports
has_many :reports, :through => :game_reports
has_many :default_reports, through: :game_reports, source: :report, conditions: { game_reports: { default: true } }
end
Rails 4.2+, use a Polymorphic association with scope and specify the foreign_key and foreign_type options.
class GameReport
belongs_to :report, :polymorphic => true
end
class Game
has_many :game_reports, :as => :report, :dependent => :destroy
has_many :reports, -> { where attachable_type: "GameReport"},
class_name: GameReport, foreign_key: :game_report_id,
foreign_type: :game_report_type, dependent: :destroy
end
Other approachs:
Rails Polymorphic Association with multiple associations on the same model

How to get single attribute from array in a has_many :through association (User/Friend model)

In my User model I have the following:
class User < ActiveRecord::Base
has_many :friendships, dependent: :destroy
has_many :friends, :class_name => "User", :foreign_key => "friend_id"
has_many :pending_friends,
:through => :friendships,
:conditions => "status = 'pending'",
:foreign_key => "user_id",
:source => :friend
has_many :requested_friends,
:through => :friendships,
:source => :friend,
:conditions => "status = 'requested'"
def friends
direct_friends | inverse_friends
end
In my Friendship model I have the following:
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => "User", :foreign_key => "friend_id"
In my view I have created an array of each of the users' friends, shown below. This code works and populates the array with all the data from the friends' "User" database model attributes.
User.first.friends
However, I want to be able to call the users' friend's name's. So, for example, shouldn't I be able to do something like this?
User.first.friends.map(&:name)
How do I get an array containing just the friend's name's, instead of all the friend's user attributes? I would also appreciate if anyone could tell me why .first is used (I got that from here: Rails calling User record from Friends model), as it doesn't just get the first instance of the User's friends (it gets all the instances). And why does just doing:
User.friends
return an empty array?
Try method pluck:
User.first.friends.pluck(:name)
You should use first method to retrieve one object from table. The User is table with a lot of users.

Rails 3 - Nested Form and has_many through association

Hi people, long time not been here. But I'm back because I need your help again please. I have a rails 3.0.9 app, and I'm working with nested forms and has_many through association. When i create an instance, it works great. The problems come when I try to edit. Here is an example for a better explanation. (table names and attributes are just for explaining)
Table Client
id
company_name
address
Table Worker
id
first_name
last name
Table Contact
id
client_id
worker_id
my models looks like these
class Worker < ActiveRecord::Base
has_many :contacts, :dependent => :destroy
has_many :clients, :through => :contacts, :foreign_key => 'client_id'
end
class Client < ActiveRecord::Base
has_many :contacts, :foreign_key => "client_id",:dependent => :destroy
has_many :workers, :through => :contacts, :foreign_key => 'worker_id'
accepts_nested_attributes_for :workers, :allow_destroy => false
end
class Contact < ActiveRecord::Base
belongs_to :worker, :foreign_key => "worker_id"
belongs_to :client, :foreign_key => "client_id"
end
Then in my form for create a client, I can create many workers, and rails make the association and creates the instances for the contacts table (by using nested forms).
The thing is, if I want to edit a client by removing a contact, the contact is not removed. As you can see I put this line in the clients model
accepts_nested_attributes_for :workers, :allow_destroy => false
I set the allow_destroy to false, because I don't want to delete the worker itself, I just want to remove the contact tuple.
Does anybody know how can I solve this?? Hope you can help me... Thanks

rails complex many-to-many query

I've got 3 models: User, Team, and Membership -
class Team < ActiveRecord::Base
has_many :memberships
has_many :members, :through => :memberships, :source => :user
end
class User < ActiveRecord::Base
has_many :memberships, :dependent => :destroy
has_many :teams, :through => :memberships
def team_mates
teams = Team.where(:members => id)
team_mates = teams.members
end
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :team
validates :user_id, :presence => true
validates :team_id, :presence => true
end
And, I can't quite figure out how to write the team_mates method in the User model. It should return an array of the other users that are in a team with the current_user. My thought is that I should be useing a scope to limit Team to only include teams where the current user is a member but I can't quite figure out the syntax. Any help on this would be greatly appreciated.
Thanks!
Use the membership table to find users who share any team with the user you are calling the method on. Then to filter out duplicate users who share more than 1 team with current user, use distinct.
I haven't tested the below code, but hopefully it will get you on the right path:
def team_mates
m = Membership.scoped.table
Users.join(m).where(m[:team_id].in(team_ids)).project('distinct users.*')
end
UPDATE
Looks like some of the Arel methods in that answer don't have an easy mapping back to ActiveRecord land. (If someone knows how, I'd love to know!) And also it returns the 'current user'. Try this instead:
def team_mates
User.joins(:memberships).where('memberships.team_id' => team_ids).where(['users.id != ?', self.id]).select('distinct users.*')
end
How about?
User.joins(:memberships).joins(:teams).where("teams.id" => id).uniq
Maybe something like this
scope team_mates, lambda {|user_id, teams| joins(:memberships).where(:team_id => teams, :user_id => user_id)}
def team_mates
User.team_mates(self.id, self.teams.collect {|t| t.id})
end
Here is more info:
http://api.rubyonrails.org/classes/ActiveRecord/NamedScope/ClassMethods.html

How to find all that has multiple relationships in many to many with Rails3 in a elegant way?

I have a User class with
class User < ActiveRecord::Base
has_many :forum_subscriptions
has_many :forums, :through => :forum_subscriptions
And a Forum class with
class Forum < ActiveRecord::Base
has_many :users
I want to find all the users that are subscribed to forum "sport", forum "TV" and forum "Hobby"
What is the most elegant way to do it?
(I have a lot of ugly stuff in my mind :-)
Here is something I would use:
User.joins(:forums).where("forums.title IN (?)", %w(sport TV Hobby)).group("users.id")
I assumed the column for the forum's title is 'title'. Change it when you're using some other name. You can put this inside a nice scope inside the User Model to make it a bit more dynamic.
scope :subscribed_to, lambda { |forum_titles| joins(:forums).where("forums.title IN (?)", forum_titles).group("users.id") }
Now you can call this scope from inside the Controller or some other place:
User.subscribed_to(%w(sport TV Hobby)) # => Get all users that are subscribed to sport, TV and Hobby
User.subscribed_to(["sport", "TV", "Hobby"]) # => Does the same
User.subscribed_to(%w(Hobby)) # => Get all users that are subscribed to Hobby
User.subscribed_to("Hobby") # => Does the same
I assumed you have the following relationships:
class User < ActiveRecord::Base
has_many :forum_subscriptions
has_many :forums, :through => :forum_subscriptions
end
class Forum < ActiveRecord::Base
has_many :forum_subscriptions
has_many :users, :through => :forum_subscriptions
end
class ForumSubscription < ActiveRecord::Base
belongs_to :user
belongs_to :forum
end
Hope this is what you needed. :)