Best way to model a user's "friendship" with different entities? - ruby-on-rails

I have the following entities:
User
Company
Organization
Users need to be able to add Users, Companies, Organizations, and future party objects to their friend list.
My original idea involved using a Friendship object with a polymorphic relationship to the friend like so:
Simplified Friendship schema:
user_id
friendable_id
friendable_type
User
- has_many :businesses, :through => :friendships, :conditions => ['friendable_type=?', 'Business'], :source => :friendable
My problem is that Ruby on Rails ActiveRecord does not support has_many through relationships through polymorphic relationships on the join table such.
In the end I want to have a single join table to be able to iterate through to get a list of friends of all types, certain types, etc. like below:
john = User.find(5)
john.friendships.map {|friendship| friendship.friendable }
john.businesses (has_many :through scoped to only the businesses)
john.organizations (has_many :through scoped only to the organizations)
I've considered making the User, Business, and Organization inherit from a generic Party class and making the association point to the base Party class, but in the context of an ORM that leads to a bunch of junk fields and it just seems dirty.
What I'd like to know is how others would approach a situation like this where you want to create one-to-many relationships to similar objects through a common join table using ActiveRecord while avoiding this error: :)
Cannot have a has_many :through association 'User#businesses' on the polymorphic object 'Friendship#friendable'.
Any advice greatly appreciated.
Thanks!

Do not use :through, use :as=>friendable instead in polymorphic has_many relationship

It appears that the Rails plugin 'has_many_polymorphs' allows me to do exactly what I want.
http://github.com/fauna/has_many_polymorphs/tree/master
Adding this line to my User model gave me the functionality I wanted:
has_many_polymorphs :friendables, :from => [:businesses, :organizations], :through => :friendships

Related

Rails has_many :through with custom attribute?

I currently have a model for a vehicle, which has a custom method for defining an owner attribute:
def owners
#owners = sales.map(&:customer) + quotes.map(&:customer)
#owners = #owners.uniq
end
This method seems to work just fine, and returns an array of owners for the vehicle.
I am having a problem, however, when I want to use the owners that this method generates in another model. When I do this on another model:
has_many :owners, :through => :vehicles
This generates the error:
ActiveRecord::HasManyThroughSourceAssociationNotFoundError: Could not find the source association(s) :owner or :owners in model Vehicle.
I have tried adding :source => :owners but I get the same error.
I should point out that I do have :owners in attr_accessible.
So, can I do a :through association when owners is defined in a custom method, rather than being a normal variable?
To answer your question directly, no.
Rails' has_many :through functionality is intended to work on ActiveRecord associations. Here, you're declaring an association to a table called owners through the vehicles table, but in actuality there is no owners table to join to: since you find the owners manually with an attribute, you aren't saving them as records in the database.
To get this working for you, I would just skip using ActiveRecord associations. Instead just define something like this on the model in question:
def owners
self.vehicles.collect(&:owners).flatten.uniq
end
You can use a through association if you start saving owners in your database, though, in which case the syntax you've provided will work.
The Rails Api says that the through: option expects you to specify an association through which to perform the query. It's trying to find this owners association based on the join table vehicles, but can't find it, since it's not there. I don't think your apporach can work without heavily diving into the HasMany-Builder from Rails.

How can I assign a Unique Level Assessment on Has-Many Polymorphic Relation, Rails

So I'm trying to come up with a smart/the best rails way to do this, and would appreciate discussing with SO users the best method.
Essentially I have two models, let's call the first a Photo and the second an Attribute. A photo has_many attributes (as examples, let's give saturation, color, warmth etc..). The Attribute model carries and name and id. The Rating model carries a single integer field. The Photo and Attribute models are related through a PhotoRelationship model, through a polymorphic association. The reason for this is that a photo has_many other relationships that I store in this table (rather than having a multitude of relationship tables). Assume I have to use these models, as they are already in place, but I can alter them or add additional models. Here is how the models' relationships work:
in Photo.rb:
has_many :photo_relationships
has_many :attributes, :through => :photo_relationships, :source => :related_attribute, :source_type => "Attribute"
in PhotoRelationship.rb
belongs_to :photo #photo_id
belongs_to :related_attribute, :polymorphic => true
in Attribute.rb
has_many :photo_relationships, :as => :related_attribute, :dependent => :destroy
has_many :photos, :through => :photo_relationships
Let's say I also want to rate the Photo model's Attributes. I introduce a third model Rating, because each photo-attribute pairing will have a certain rating then associated with it.
I want to figure out how to run the relationship between a photo-attribute pairing and its rating. For example, let's say we have a photo with a certain number of attributes and we want to find the rating of, say, the first attribute of that photo. Then:
photo = Photo.first
photo.attributes.first.rating
How would I set it up in the database? I suppose I could add a rating_id field to the PhotoRelationship model, which already has unique entries for each photo-attribute pair.
Could this work, could I somehow relate the attribute to its rating?? Something tells me something is missing. I am getting the feeling that I would need to have a separate AttributeRelationship table that is non-polymorphic and even in this case, I don't see how the relationships work.
Any info anyone can provide, thoughts, or alternate suggestions on how to do this would be greatly appreciated! Thanks!
I suggest you use one of the gems already available that provide ratings functionality:
https://www.ruby-toolbox.com/categories/rails_ratings
If you want to add rating functionality to existing models from scratch then think about using mixins(modules), because your model associations are quite complicated as it is.
Useful info about mixin usage here:
ruby inheritance vs mixins

Whats the cleanest way to handle many-to-many relationships in ruby on rails?

I have one model say user, that can live in multiple towns (represented as another model). If I create a new user I have to choose (and edit) the different towns that they live in. Due to time constraints, I often end up with a "hackyier than I would like" solution involving something like: http://blog.hasmanythrough.com/2006/4/20/many-to-many-dance-off.
Any nice solutions that are popular with SO?
cheers...
Slothishtype
The has_and_belongs_to_many association was built for this very situation. Here is the documentation on it: http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_and_belongs_to_many
Otherwise, if you need to store information abotu the association itself (fields that would not exist in the city table or the user table, but in between), you might just want to set up two, parallel 'has_many_through' associations, and set up a seperate 'user_city' table. So it would be in the user table
has_many :user_cities
has_many :cities, :through => :user_cities
and in the cities table
has_many :user_cities
has_many :users, :through => :user_cities
Then, you CAN just call: user.cities, and get a list of the cities the user lives in.

user has many :users, or must I use another way for a friend based social network?

I'm creating a little social network in Rails, where people can add eachother as a friend. I have created a model called 'user', which contains, e-mail, strong md5 hash with salt of password etc.
How do I create something like an option to add another user as a friend? Is it possible to have something like has_many_and_belongs_to :user in the user model? So a user has many users and belongs to many users. Or should I use another way, like adding a friendship model which has user1s_id:integer and user2s_id:integer?
Essentially you want a join model that joins users to users. But you'll have to use more descriptive terms for Rails.
Twitter doesn't use Rails, but here's how their user associations might work in Rails using Twitter's terminology. Note: Twitter doesn't require bidirectional following (ie just because a user follows another, doesn't mean that second user follows the first)
class User < ActiveRecord::Base
has_many :followings
has_many :followers, :through => :followings, :class_name => "User"
has_many :followees, :through => :followings, :class_name => "User"
end
class Following < ActiveRecord::Base
# fields: follower_id followee_id (person being followed)
belongs_to :follower, :class_name => "User"
belongs_to :followee, :class_name => "User"
end
Forced bidirectional friendship (like Facebook enforces that I cannot be friends with you, unless you are a friends with me) will take a little more work. You will either need to manage reciprocal entries with callbacks, or use custom finder SQL. Using custom finder SQL means that ActiveRecord probably won't be able to manage associations for you.
I would suggest that a user has many relationships, this would leave you free to add new types of relationships in the future.
So you start with a "one user to another user" relationship table (minimally, just two IDs) and then in the future you could add a "one user to a group" relationship table (once you've created groups of course!)
I'd suggest using the friendship model because db-wise you'll need a join table either way, but making that table explicit will allow you to store more details about the relationship (e.g. "friend" or "family", date added, ...)

How should I architect this with Ruby on Rails?

Sorry for the vague title, but this is kind of hard to put into one line.
I have a database full of contact information, and I want to be able to put those different contacts into groups which will also be stored in the database.
So, maybe I have 2 groups, "coworkers" and "neighbors", I want to be able to see a list of all my contacts and be able to add individual contacts to one or more groups.
I'm kind of confused as where to begin with this, could I get some basic outlines of how this might best be implemented? Thanks.
Well, you've got two models, Contact and Group. These two guys are clearly going to have their own tables (probably 'contacts' and 'groups' if you're following the Rails conventions). Since a contact can be in many groups, and a group can have many contacts, you've got what's called a many-to-many relationship between these models. There are basically two ways to implement this in Rails: has_and_belongs_to_many or has_many :through. Which one is best for you will depend a little on your situation.
has_and_belongs_to_many is probably the path of least resistance. You put a couple lines in your models like so:
# contact.rb
has_and_belongs_to_many :groups
# group.rb
has_and_belongs_to_many :contacts
...and create a table called 'contacts_groups' with the columns contact_id and group_id, and you're basically good to go. Easy peasy.
On the other hand, there are some advantages to using an association model with has_many :through. In this approach, you create another model, say, GroupMembership, and set up your models like so:
# contact.rb
has_many :group_memberships # this isn't strictly required, but I'd recommend it
has_many :groups, :through => :group_memberships
# group_membership.rb
has_many :groups
has_many :contacts
# group.rb
has_many :group_memberships # again, recommended but not required
has_many :contacts, :through => :group_memberships
This gives you most of the same convenience methods as has_and_belongs_to_many, and also lets you store any extra data you might want about the association, like the date they joined the group or the reason they were added. Even if you don't have any of that now, it's nice to account for the possibility of adding it later. Also, this lets you take a more RESTful approach to adding and removing contacts to and from groups, if that's something you're interested in, since you can model it in terms of creating and destroying GroupMembership resources.
By and large, I tend to lean toward the latter approach, especially as I've gotten more in to RESTful architectures. On the other hand, if you're not worried about REST and you're certain you'll never want to keep any extra information about memberships, has_and_belongs_to_many is probably simpler and requires less code to get working. For more in-depth information on the differences and implementation details, see the ActiveRecord Associations API docs.
You could structure a Ruby on Rails database to be :
Contact ( first_name:string, last_name:string, title:enum number:string, cell:string, notes:text, email:string ) => many_many :groups (or has_many :groups, :through=> :contact_group)
Contact_Group { group_id:integer, contact_id:integer }
Group ( name:string ) => many_many :contacts ) (or has_many :contacts, :through=> :contact_group)
That might be the general idea. You also could do relational fields.
You don't want to use the has_and_belongs_to_many approach. It's deprecated. Please read the API and make sure you implement a join model / has_many :through approach. The API will tell you how to do this and also mention why has_and_belongs_to_many is bad.
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

Resources