Finding the repliers to a comment from a polymorphic comments model - ruby-on-rails

I have this polymorphic association where a user can have many comments, a school can have many comments, and a comment can have many comments (or in my naming case replies):
class Comment < ActiveRecord::Base
attr_accessible :content
has_many :replies, :as => :commentable, :class_name => "Comment" # replies to comments
belongs_to :commentable, :polymorphic => true
belongs_to :commentor, :class_name => "User", :foreign_key => "user_id"
end
class User < ActiveRecord::Base
has_many :comments, :as => :commentable
has_many :commentors, # all users who commented on a user
:through => :comments,
:source => :commentor
end
class School < ActiveRecord::Base
has_many :comments, :as => :commentable
has_many :commentors, # all users who commented on a school
:through => :comments,
:source => :commentor
end
In the User, I can retrieve all who commented on a user using #user.commentors. Same goes for School, i.e. #school.commentors.
For the comments model, I would like to acheive the same thing for the Comments model where I can find all the commentors (or I guess repliers) to a comment; however, I have no idea what kind of association to create since a has_many :through association will not work like how it worked for the User and School model.

Use this:
has_many :reply_commentors, :through => :replies, :source => :commentor

Related

Rails: Association was not found; perhaps you misspelled it?

I have a User model with a has_many :through relationship to the Publication model. The Publication model in turn has a has_many :through relationship to Author:
class User < ActiveRecord::Base
has_many :library_publications, :dependent => :destroy, :class_name => "Library::Publication"
has_many :publications, :through => :library_publications
end
class Library::Publication < ActiveRecord::Base
belongs_to :publication
belongs_to :user
end
class Publication < PublicationBase
has_many :library_publications, :dependent => :destroy, :class_name => "Library::Publication"
has_many :users, :through => :library_publications
has_many :publication_contributions, :dependent => :destroy, :class_name => "Publication::Contribution"
has_many :authors, :through => :publication_contributions
end
class Author < AuthorBase
has_many :publication_contributions, :dependent => :destroy, :class_name => "Publication::Contribution"
has_many :publications, :through => :publication_contributions
end
class Publication::Contribution < Publication::ContributionBase
belongs_to :publication, :class_name => "Publication"
belongs_to :author, :class_name => "Author"
end
As far as I can tell, all the associations are written correctly. However, when I try to eagerload authors from a user:
#user.library_publications.includes(:publication => [:authors])
I get this error:
Association named 'authors' was not found; perhaps you misspelled it?
What might be the cause of this?
After experimenting a little, I discovered that all of publication's associations were broken. This led to me to looking for larger problems, and eventually I discovered that this issue was caused by one of the join-table being namespaced, Library::Publication. When I de-namespaced it, publication's associations began working again.
I'm not sure why this happened, though. If anyone has an explanation, please share.

What do polymorphic associations solve?

Let's assume I have an application with a "favorites" feature, where users can add a document, note, or comment to his favorites list.
In my mind..
User has_many Favorites
Favorite belongs_to a User
Document belongs_to a Favorite
Note belongs_to a Favorite
Comment belongs_to a Favorite
What's the problem with this type of association and how would polymorphic associations help?
because then your Favorite instance will not know what it is favouriting :)
it knows that it has_one :note, but also has_one :comment, or? but not both surely.
polymorphic association the opposite way helps because it will express that a Favorite object belongs_to :favorited object that is polymorphic cos it can be any class the name of which will be stored in the :favorited_type string db column, so your favorite object will know that it favors a note or document or comment.
with some code
class Note
has_many :favorites, :as => :favorited
has_many :fans, :through => :favorites, :source => :user
end
class Discussion
has_many :favorites, :as => :favorited
has_many :fans, :through => :favorites, :source => :user
end
class Comment
has_many :favorites, :as => :favorited
has_many :fans, :through => :favorites, :source => :user
end
class Favorite
belongs_to :user
belongs_to :favorited, :polymorphic => true # note direction of polymorphy
end
class User
has_many :favorites
has_many :favorite_notes, :through => :favorites, :source => favorited, :source_type => "Note"
has_many :favorite_comments, :through => :favorites, :source => favorited, :source_type => "Comment"
has_many :favorite_discussions, :through => :favorites, :source => favorited, :source_type => "Discussion"
end
(just set up your db correctly) this design is standard for such usecase of favoriting.

What is the best way to handle 4 way relation between 2 models?

I have two models: Company and User
This is the situation:
Company can follow another company
User can follow a company
User can follow another user
What is the best way to define the relationships and how will the join model look like?
Also, are there any best practises when addressing such situations?
Update
Sorry, to have not mentioned this earlier. I am aware of the various relationship types available. My question is 'which is the best fit'?
Regarding your question I would suggest you to go through couple of Railscasts videos:
http://railscasts.com/episodes/47-two-many-to-many
http://railscasts.com/episodes/154-polymorphic-association
And this is described very well on RubyonRails website
http://guides.rubyonrails.org/association_basics.html
I would say look these for your case:
http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association
http://guides.rubyonrails.org/association_basics.html#the-has_and_belongs_to_many-association
I hope this will help you.
Thanks to polymorphic associations, we can put all relations into one table which like this:
create_table :follows do |t|
t.references :followable, :polymorphic => true
t.references :followed_by, :polymorphic => true
end
Then the models are:
class User < ActiveRecord::Base
has_many :following_objects, :class_name => 'Follow', :as => :followed_by
has_many :followed_objects, :class_name => 'Follow', :as => :followable
end
class Company < ActiveRecord::Base
has_many :following_objects, :class_name => 'Follow', :as => :followed_by
has_many :followed_objects, :class_name => 'Follow', :as => :followable
end
class Follow < ActiveRecord::Base
belongs_to :followable, :polymorphic => true
belongs_to :followed_by, :polymorphic => true
end
Sorry for the ugly names.
A basic idea would be to use two self-referencing assocations:
User -> Friendship <- User
Company -> Partnership <- Company
models/user.rb
has_many :friendships
has_many :friends, :through => :friendships
has_many :inverse_friendships, :class_name => "Friendship", :foreign_key => "friend_id"
has_many :inverse_friends, :through => :inverse_friendships, :source => :user
models/friendship.rb
belongs_to :user
belongs_to :friend, :class_name => "User"
models/company.rb
has_many :partnerships
has_many :partners, :through => :partnerships
has_many :inverse_partnerships, :class_name => "Partnership", :foreign_key => "partner_id"
has_many :inverse_partners, :through => :inverse_partnerships, :source => :company
models/partnership.rb
belongs_to :company
belongs_to :partner, :class_name => "Company"
And one many-to-many assocation:
User -> CompanyUser <- Company
models/user.rb
has_and_belongs_to_many :companies
models/company.rb
has_and_belongs_to_many :users
So for this implementation you will need 5 tables (users, friendships, companies, partnerships and companies_users) if you are using a RDBMS.
You can get a nice example in this screencast:
http://railscasts.com/episodes/163-self-referential-association

In rails 2.3, how can I retrieve a collection of objects from a second-order has_many association?

I have a Partner model that has_and_belongs_to_many Projects, while each Project has_many Sites. I want to retrieve all sites for a given partner (and am not interested in the projects in between at the moment).
I have accomplished what I need through a named_scope on the Site model, and a project.sites instance method that wraps a call to the Site named scope, as follows:
class Partner < ActiveRecord::Base
has_and_belongs_to_many :projects
def sites
Site.for_partner_id(self.id)
end
end
class Project < ActiveRecord::Base
has_many :sites
end
class Site < ActiveRecord::Base
belongs_to :project
named_scope :for_partner_id, lambda {|partner_id|
{ :include=>{:project=>:partners},
:conditions=>"partners.id = #{partner_id}"
}
}
end
Now, given a partner instance, I can call partner.sites and get back a collection of all sites associated with the partner. This is precisely the behavior I want, but I'm wondering if there's another way to do this using only activerecord associations, without the named scope?
I had a similar deep nesting query/collection problem here (I had to threaten to repeat data before anyone would answer my 4 questions, clever):
Is it appropriate to repeat data in models to satisfy using law of demeter in collections?
The trick is this gem http://rubygems.org/gems/nested_has_many_through which can do something like this:
class Author < User
has_many :posts
has_many :categories, :through => :posts, :uniq => true
has_many :similar_posts, :through => :categories, :source => :posts
has_many :similar_authors, :through => :similar_posts, :source => :author, :uniq => true
has_many :posts_of_similar_authors, :through => :similar_authors, :source => :posts, :uniq => true
has_many :commenters, :through => :posts, :uniq => true
end
class Post < ActiveRecord::Base
belongs_to :author
belongs_to :category
has_many :comments
has_many :commenters, :through => :comments, :source => :user, :uniq => true
end
This has super-simplified my queries and collections. I hope you find an answer to your problem, it's a tough one!

How do I define ActiveRecord relationships between two models that relate to each other in two different ways?

In my app I have the classes User, Video, and Vote. Users and Videos can relate to each other in two different ways: as a one-to-many or as a many-to-many. The former is when a User submits a Video (one user can submit many videos). The latter is when a user votes on a video (users have many videos through votes, and vice versa). Here is my code, which does not work (I think -- I may be doing something wrong in the view). Please help me understand the correct way to structure these associations:
class User < ActiveRecord::Base
has_many :videos, :as => :submissions
has_many :votes #have tried it without this
has_many :videos, :as => :likes, :through => :votes
end
class Vote < ActiveRecord::Base
belongs_to :video
belongs_to :user
end
class Video < ActiveRecord::Base
belongs_to :user
has_many :votes #have tried it without this . . . superfluous?
has_many :users, :as => :voters, :through => :votes
end
I haven't gone and checked, but it goes something like this:
Instead of
has_many :videos, :as => :likes, :through => :votes
Use
has_many :likes, :class_name => "Video", :through => :votes
Same with the bottom:
has_many :users, :as => :voters, :through => :votes
becomes
has_many :voters, :class_name => "User", :through => :votes
:as is used for polymorphic associations. See this chapter in docs for more info.
class User < ActiveRecord::Base
has_many :videos # Submitted videos
has_many :votes
has_many :voted_videos, :through => :votes # User may vote down a vid, so it's not right to call 'likes'
end
class Vote < ActiveRecord::Base
belongs_to :video
belongs_to :user
end
class Video < ActiveRecord::Base
belongs_to :user
has_many :votes
has_many :voters, :through => :votes
end
More details can be found here: http://guides.rubyonrails.org/association_basics.html
Hope it helps =)
Thanks for your help guys, definitely pointed me in the right direction. Here is the working code:
class User < ActiveRecord::Base
has_many :videos, :as => :submissions
has_many :votes
has_many :likes, :source => :video, :through => :votes
end
class Vote < ActiveRecord::Base
belongs_to :video
belongs_to :user
end
class Video < ActiveRecord::Base
belongs_to :user
has_many :votes
has_many :voters, :source => :user, :through => :votes
end
PS I kept it as :likes because in this app they won't be able to downvote, only upvote.

Resources