Naming polymorphic relationships well - ruby-on-rails

This question is about the naming style of polymorphic relationships.
My database has three types of person: a 'Company', a Client, and an Employee. Each of the three are in polymorphic relationships with tasks and events, and projects.
According to the Rails guides, this would be done like (I've omitted some classes for brevity):
Person.rb
has_many :tasks, :as => :taskable
has_many :events, :as => :eventable
has_many :projects, :as => :projectable # awkward names
Task.rb
belongs_to :taskable, :polymorphic => true
These lead to the rather strange:
#person = #task.taskable
I feel that the following would be far more grammatical and elegant... would it work, and if so, is there a reason that official sources use words like projectable rather than words like owner?
Person.rb
has_many :tasks, :as => :owner
has_many :events, :as => :owner
has_many :projects, :as => :owner
Task.rb
belongs_to :owner, :polymorphic => true
This creates the elegant:
#person_1 = #task.owner
#person_2 = #project.owner

I personally try to keep it as generic as possible.
So :as => :owner does make more sense to me.
In case of doubt, I'd just use
:as => :parent
which I've already seen in some projects.

Related

has_many, :through => :multiple

I would like a relationship along the lines of
has_many :foos, :through => :multiple
models
Site:
has_many :pages
has_many :files
Page:
belongs_to :site
has_many :legacy_url_redirects, as: :redirect_resourceable
File:
belongs_to :site
has_many :legacy_url_redirects, as: :redirect_resourceable
LegacyUrlRedirects
belongs_to :redirect_resourceable, :polymorphic => true
What I would like to do is in the Site model add something like:
has_many :legacy_url_redirects, :through => [:pages, :files]
Or an equally good alternative.
Thanks.
EDIT:
Here is what i did:
Site model:
def legacy_url_redirects(options = {})
file_where = {"files.site_id" => self.id}.merge(options)
page_where = {"pages.site_id" => self.id}.merge(options)
LegacyUrlRedirect.includes(:file).where(file_where) + LegacyUrlRedirect.includes(:page).where(page_where)
end
I also added the :pages and :files relationships to the LegacyUrlRedirects model with help from: https://stackoverflow.com/a/16124295/1141264
SECOND EDIT:
I refactored the fix a little so that its a manually written LEFT OUTER JOIN between the three tables. Also not specifying the select part of the query when doing a join implies #readonly = true on the records returned.

Expected model.rb to define MODEL

I have 3 tables Collections, Tracks and ProductContributors
Association of them is as follows
class Collection < ActiveRecord::Base
has_many :product_contributors, :as => :product
has_many :tracks, :through => Product_contributors, :as=> :product
end
class Track < ActiveRecord::Base
has_many :product_contributors, :as => :product
has_many :collections, :through => Product_contributors, :as => :product
end
class ProductContributor < < ActiveRecord::Base
belongs_to :product, :polymorphic => true
belongs_to :collection
belongs_to :track
end
whenever i hit the url for product contributor i get the following error :
Expected /app/models/track.rb to define TRACK
I've gone through this url but didnt help me in any case. I dont have the autoload issue, all my models are loaded properl
Any help would be highly appreciated..!!
I dare say its because of the typo in your Track class.
has_many :collections, :through => Product_contributors, :as => :product
is not valid. Try:
has_many :collections, :through => :product_contributors, :as => :product
Basically, it is trying to load the Model, but its finding the typo in the association, and it is then not loading, causing it to seem like the class is not there. I assume you will have a similar situation with the Collection class as well.

Two 1 - N relations in Mongoid (Rails)

The scenario is:
How can an Account give ratings to another account? This results in two lists on the Account. Those who I have rated and those who have rated me. (my_ratings and ratings_given)
This boils down to:
How can multiple 1 - N relationsips to the same entity work in Mongoid?
In Mongoid's Docs it says you can use has_many and belongs_to to link the entities together.
I currently have this on Account
has_many :ratings, :as => "my_ratings"
has_many :ratings, :as => "ratings_given"
and this on Ratings:
belongs_to :user, :as => 'Rater'
belongs_to :user, :as => 'Ratie'
The docs don't cover this case, so I thought you would have to differentiate between the two with an :as parameter.
Is this even remoting correct?
You can achieve what you want using the class_name and inverse_of options:
class Account
include Mongoid::Document
field :name
has_many :ratings_given, :class_name => 'Ratings', :inverse_of => :rater
has_many :my_ratings, :class_name => 'Ratings', :inverse_of => :ratee
end
class Ratings
include Mongoid::Document
field :name
belongs_to :rater, :class_name => 'Account', :inverse_of => :ratings_given
belongs_to :ratee, :class_name => 'Account', :inverse_of => :my_ratings
end
The documentation has changed since I was last working with it so I wasn't sure whether this is still the recommended approach. Looks like it doesn't mention these options on the 1-many referenced page. But if you take a look at the general page on relations they are covered there.
In any case you need to explicitly link ratings_given/rater and my_ratings/ratee associations when there are two associations to the same class, otherwise mongoid has no way to know which of the two potential inverses to pick.

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!

Models -> has_many -> Twice

So I have a somewhat confusing relationship here, between a Note, Group, and User. And I ended up with has_many twice in my model. But I'm currently focused on the Note & Group relationship.
Background: A Group can have a note. A User can also have a note. Which is why my Note is polymorphic. However, I also created a join model called a Tag, so that a Note can belong to multiple groups. In my code though, I ended up with multiple 'has_many :notes'. See all of my code below. What would be the proper way to do something like this?
Thanks in advance!
note.rb
belongs_to :notable, :polymorphic => true
has_many :tags
has_many :groups, :through => :tags
user.rb
has_many :notes, :as => :notable
group.rb
has_many :notes, :as => :notable
has_many :tags
has_many :notes, :through => :tags
tag.rb
belongs_to :note
belongs_to :group
You just need to give it a different name.
class Group
has_many :notes, :as => :notable
has_many :tags
has_many :tagged_notes, :class_name => 'Note', :through => :tags
end
If you only want a single note for the :as => :notable part (this wasn't very clear in your question), you could just do this:
class Group
has_one :note, :as => :notable
has_many :tags
has_many :notes, :through => :tags
end
The names just have to be different. Although with note vs. notes it might not be very clear what the distinction is in other parts of your code.

Resources