Ok, I just want to set this one in stone.
Rails’ polymorphic associations.
Ask yourself 2 questions:
class Car < ActiveRecord::Base
end
class House < ActiveRecord::Base
end
class LineItem < ActiveRecord::Base
belongs_to :salable, :polymorphic => true
end
Here’s a domain model from an app in which you can purchase both cars and houses. They are both salable products that will appear on a line item in an order. This is a many-to-1 relationship from a LineItem to its salable product.
class User < ActiveRecord::Base
include Groupable
end
class Account < ActiveRecord::Base
include Groupable
end
class Membership < ActiveRecord::Base
belongs_to :groupable, :polymorphic => true
belongs_to :group
end
class Group < ActiveRecord::Base
has_many :memberships
end
module Groupable
def self.included(clazz)
clazz.class_eval
has_many :memberships, :as => :groupable
end
end
end
Here we have a many-to-many between groupable objects and groups. Since a Group can contain members of different types it needs to be polymorphic. However, in this example a single group can have multiple members all of different types. Like any many-to-many we need a table between the 2 other tables. Since habtm doesn’t support polymorphic associations we have to go with a join model. So we introduced Membership and made it have a polymorphic relationship.
It’d be sweet if we didnt need this join model because I can’t stand unnecessary classes, something like:
class User < ActiveRecord::Base
include Groupable
end
class Account < ActiveRecord::Base
include Groupable
end
class Group < ActiveRecord::Base
has_and_belongs_to_many :groupables
end
module Groupable
def self.included(clazz)
clazz.class_eval
has_and_belongs_to_many :groups, :as => :groupable
end
end
end
db schema:
groupables (groupable_id, groupable_type, group_id)That’s some made up syntax. I’ll say if you have an
:as parameter to #has_and_belongs_to_many then Rails will look for a table named after the polymorphic interface, in this case groupables, and 2 columns groupable_id and groupable_type. Since the #has_and_belongs_to_many call in the Group model doesn’t include an :as parameter, Rails will, like in a normal #has_and_belongs_to_many call, look for a table named after the association, in this case groupables, and a foreign key in that table referencing this model, in this case group_id.