has_relationship is an easy way to instantly create an association between any two database tables
Ruby
Fetching latest commit…
Cannot retrieve the latest commit at this time.

README.rdoc

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

  1. rails generate has_relationship:migration

  2. 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.