HasRelationship
This gem was based on insight provided by the article blog.hasmanythrough.com/2006/4/3/polymorphic-through. It was developed because of a lack of solutions to be able to handle double polymorphism in rails. In other words, there was no way to create an xref table (the middle table in a many-to-many relationship) that joined two unknown tables together. There are obvious reasons for these limitations in rails, but nevertheless, a solution was needed.
HasRelationship hopefully is that solution. The user runs a migration that generates the “relationships” table. That table is then used to join any two tables together and create a relationship between them. So now, when you want to add a relationship between class 1 and class 2, you won’t have to generate your own custom intermediate xref table each time; the “relationships” table can be reused for each of your custom relationships.
Installation
Rails 3.0
Add the following to your Gemfile:
gem 'has-relationship'
Post Installation
-
rails generate has_relationship:migration
-
rake db:migrate
Usage
class User < ActiveRecord::Base
# Create a has-one-through relationship with the "tasks" table (Task class) through the "relationships" table
has_relationship :task
# Alternate approach to create a has-one-through relationship with the "tasks" table (Task class) through the "relationships" table
has_relationship "task"
# Alternate approach to create a has-one-through relationship with the "tasks" table (Task class) through the "relationships" table
has_relationship Task
# Alternate approach to create a has-one-through relationship with the "tasks" table (Task class) through the "relationships" table
has_relationship :tasks, :singular => true
# Create a has-many-through relationship with the "tasks" table (Task class) through the "relationships" table
has_relationship :tasks
# Create a has-many-through relationship called "other_tasks" with the "tasks" table (Task class) through the "relationships" table.
# Please note the addition of the :relationship attribute. This is optional but highly recommended, particularly when you're going
# to be declaring a "has_inverse_relationship" on the Task class, as shown below. This parameter sets the "relationship" field in
# the relationship table.
# If :relationship had not been set here, it still would have defaulted to "User_OtherTask"; the benefit however of manually declaring this
# is that it clear exactly what to name the relationship in your use of "has_inverse_relationship".
has_relationship :other_tasks, :class_name => "Task", :relationship => "User_OtherTask"
# Alternate approach to create a has-many-through relationship called "other_tasks" with the "tasks" table (Task class) through the "relationships" table
has_relationship :tasks, :as => :other_tasks, :relationship => "User_OtherTask"
end
class Task < ActiveRecord::Base
# Create a has-one-through relationship with the "users" table (User class) through the "relationships" table
has_inverse_relationship :user
# Alternate approach to create a has-one-through relationship with the "users" table (User class) through the "relationships" table
has_inverse_relationship "user"
# Alternate approach to create a has-one-through relationship with the "users" table (User class) through the "relationships" table
has_inverse_relationship User
# Alternate approach to create a has-one-through relationship with the "user" table (User class) through the "relationships" table
has_inverse_relationship :users, :singular => true
# Create a has-many-through relationship with the "users" table (User class) through the "relationships" table
has_inverse_relationship :users
# Create a has-many-through relationship called "other_users" with the "users" table (User class) through the "relationships" table
# Please note that here we specify the exact same :relationship as was used in the User class in its call to "has_relationship".
has_inverse_relationship :other_users, :class_name => "User", :relationship => "User_OtherTask"
# Alternate approach to create a has-many-through relationship called "other_users" with the "users" table (User class) through the "relationships" table
has_inverse_relationship :users, :as => :other_users, :relationship => "User_OtherTask"
# Create a has-one-through relationship with the "assignments" table (Assignment class) through the "relationships" table
has_inverse_relationship :assignment
# Create a has-many-through relationship with the "stories" table (Story Class) through the "relationships" table
has_relationship :stories
end
class Assignment < ActiveRecord::Base
# Create a has-many-through relationship with the "tasks" table (Task class) through the "relationships" table,
# and create another has-many-through relationship with the "stories" table (Story class) through the "relationships" table
has_relationship [:tasks, :stories]
# Create a has-many-through relationship called "other_tasks" with the "tasks" table (Task class) through the "relationships" table
has_relationship :tasks, :as => :other_tasks
end
class Story < ActiveRecord::Base
# Create a has-one-through relationship called "parent" with the "tasks" table (Task class) through the "relationships" table.
has_inverse_relationship :tasks, :as => :parent, :relationship => "Task_Story"
# Create a has-one-through relationship with the "tasks" table (Task class) through the "relationships" table.
has_inverse_relationship :task
end
@user = User.new(:name => "Bobby")
@user.task = Task.new
@user.tasks.build
@user.other_tasks << Task.new
@user.save
@assignment = Assignment.new
@assignment.tasks << Task.create
@assignment.stories.build
@assignment.other_tasks << Task.new
@assignment.save
@story = Story.new
@story.parent = Task.create
@story.task = Task.new
@story.save
Copyright © 2011 Andrew Hunter (github.com/hunterae) and Captico LLC. (captico.com/), released under the MIT license
Special Thanks
HasRelationship was aided by some of the insight provided in the article blog.hasmanythrough.com/2006/4/3/polymorphic-through written by Josh Susser. Also many thanks to the ActsAsTaggableOn team, as I have used your gem as a jumping point for writing my own, given the similar nature of the task at hand.