public
Description: An ActiveRecord plugin for self-referential and double-sided polymorphic associations.
Homepage: http://blog.evanweaver.com/files/doc/fauna/has_many_polymorphs/
Clone URL: git://github.com/fauna/has_many_polymorphs.git
stepheneb (author)
Fri Aug 14 14:09:00 -0700 2009
fauna (committer)
Fri Aug 14 16:14:15 -0700 2009
commit  d22679466f11b1c0508cfc90fbfd5990fcb38230
tree    bb3fbae39a0340aeb79d20786c29f97737791658
parent  f59c018a757cf5bde564bc26130a6a8e5fdc11fa
name age message
file .gitignore Mon Feb 02 00:51:06 -0800 2009 Ignoring test/integration/app/log/ Signed-off-... [Nielsomat]
file CHANGELOG Wed Apr 01 07:27:05 -0700 2009 Changelog. [Evan Weaver]
file LICENSE Thu Aug 02 19:00:20 -0700 2007 trunkify; merge polymorphs [evan]
file Manifest Mon Feb 02 00:57:07 -0800 2009 Manifest was wrong. [Evan Weaver]
file README Mon Feb 02 00:55:42 -0800 2009 Update required Rails version (older versions m... [fauna]
file Rakefile Tue Jan 15 16:32:04 -0800 2008 clean pattern excludes generated models [evan]
file TODO Tue Jan 15 16:29:54 -0800 2008 migration examples, finally! [evan]
directory examples/ Fri Sep 14 22:17:30 -0700 2007 moved hmph.rb to trunk/examples and made sure I... [Beppu]
directory generators/ Loading commit data...
file init.rb Thu Aug 02 19:00:20 -0700 2007 trunkify; merge polymorphs [evan]
directory lib/
directory test/
README
Has_many_polymorphs

An ActiveRecord plugin for self-referential and double-sided polymorphic associations.

== License

Copyright 2006-2008 Cloudburst, LLC. Licensed under the AFL 3. See the included LICENSE file. 

The public certificate for the gem is 
here[http://rubyforge.org/frs/download.php/25331/evan_weaver-original-public_cert.pem]. 

If you use this software, please {make a donation}[http://blog.evanweaver.com/donate/], or {recommend 
Evan}[http://www.workingwithrails.com/person/7739-evan-weaver] at Working with Rails.

== Description

This plugin lets you define self-referential and double-sided polymorphic associations in your models. It is an 
extension of <tt>has_many :through</tt>.

“Polymorphic” means an association can freely point to any of several unrelated model classes, instead of being tied to o
ne particular class.

== Features

* self-references
* double-sided polymorphism
* efficient database usage
* STI support
* namespace support
* automatic individual and reverse associations

The plugin also includes a generator for a tagging system, a common use case (see below).

== Requirements

* Rails 2.2.2 or greater

= Usage

== Installation

To install the Rails plugin, run:
  script/plugin install git://github.com/fauna/has_many_polymorphs.git

There's also a gem version. To install it instead, run: 
  sudo gem install has_many_polymorphs
  
If you are using the gem, make sure to add <tt>require 'has_many_polymorphs'</tt> to <tt>environment.rb</tt>, before 
Rails::Initializer block.

== Configuration

Setup the parent model as so:

  class Kennel < ActiveRecord::Base
    has_many_polymorphs :guests, :from => [:dogs, :cats, :birds]
  end

The join model:

  class GuestsKennel < ActiveRecord::Base
    belongs_to :kennel
    belongs_to :guest, :polymorphic => true
  end

One of the child models:

  class Dog < ActiveRecord::Base
    # nothing
  end

For your parent and child models, you don't need any special fields in your migration. For the join model 
(GuestsKennel), use a migration like so:

  class CreateGuestsKennels < ActiveRecord::Migration
    def self.up
      create_table :guests_kennels do |t|
        t.references :guest, :polymorphic => true
        t.references :kennel
      end
    end
  
    def self.down
      drop_table :guests_kennels
    end
  end

See ActiveRecord::Associations::PolymorphicClassMethods for more configuration options.

== Helper methods example

  >> k = Kennel.find(1)
  #<Kennel id: 1, name: "Happy Paws">
  >> k.guests.map(&:class) 
  [Dog, Cat, Cat, Bird]
  
  >> k.guests.push(Cat.create); k.cats.size
  3
  >> k.guests << Cat.create; k.cats.size
  4
  >> k.guests.size
  6

  >> d = k.dogs.first
  #<Dog id: 3, name: "Rover">
  >> d.kennels 
  [#<Kennel id: 1, name: "Happy Paws">]
  
  >> k.guests.delete(d); k.dogs.size
  0
  >> k.guests.size
  5  
  
Note that the parent method is always plural, even if there is only one parent (<tt>Dog#kennels</tt>, not 
<tt>Dog#kennel</tt>).

See ActiveRecord::Associations::PolymorphicAssociation for more helper method details.

= Extras

== Double-sided polymorphism

Double-sided relationships are defined on the join model:

  class Devouring < ActiveRecord::Base
    belongs_to :guest, :polymorphic => true
    belongs_to :eaten, :polymorphic => true
  
    acts_as_double_polymorphic_join(
      :guests =>[:dogs, :cats], 
      :eatens => [:cats, :birds]
    )       
  end
  
Now, dogs and cats can eat birds and cats. Birds can't eat anything (they aren't <tt>guests</tt>) and dogs can't be 
eaten by anything (since they aren't <tt>eatens</tt>). The keys stand for what the models are, not what they do. 

In this case, each guest/eaten relationship is called a Devouring.

In your migration, you need to declare both sides as polymorphic:

  class CreateDevourings < ActiveRecord::Migration
    def self.up
      create_table :devourings do |t|
        t.references :guest, :polymorphic => true
        t.references :eaten, :polymorphic => true
      end
    end
  
    def self.down
      drop_table :devourings
    end
  end

See ActiveRecord::Associations::PolymorphicClassMethods for more.

== Tagging generator

Has_many_polymorphs includes a tagging system generator. Run:
  script/generate tagging Dog Cat [...MoreModels...]

This adds a migration and new Tag and Tagging models in <tt>app/models</tt>. It configures Tag with an appropriate 
<tt>has_many_polymorphs</tt> call against the models you list at the command line. It also adds the file 
<tt>lib/tagging_extensions.rb</tt> and <tt>requires</tt> it in <tt>environment.rb</tt>. 

Tests will also be generated. 

Once you've run the generator, you can tag records as follows:

  >> d = Dog.create(:name => "Rover")
  #<Dog id: 3, name: "Rover">
  >> d.tag_list
  ""
  >> d.tag_with "fierce loud"
  #<Dog id: 3, name: "Rover">
  >> d.tag_list
  "fierce loud"
  >> c = Cat.create(:name => "Chloe")
  #<Cat id: 1, name: "Chloe">
  >> c.tag_with "fierce cute"
  #<Cat id: 1, name: "Chloe">
  >> c.tag_list
  "cute fierce"
  >> Tag.find_by_name("fierce").taggables 
  [#<Cat id: 1, name: "Chloe">, #<Dog id: 3, name: "Rover">]

The generator accepts the optional flag <tt>--skip-migration</tt> to skip generating a migration (for example, if you 
are converting from <tt>acts_as_taggable</tt>). It also accepts the flag <tt>--self-referential</tt> if you want to be 
able to tag tags.

See ActiveRecord::Base::TaggingExtensions, Tag, and Tagging for more.

== Troubleshooting

Some debugging tools are available in <tt>lib/has_many_polymorphs/debugging_tools.rb</tt>.

If you are having trouble, think very carefully about how your model classes, key columns, and table names relate. You 
may have to explicitly specify options on your join model such as <tt>:class_name</tt>, <tt>:foreign_key</tt>, or 
<tt>:as</tt>. The included tests are a good place to look for examples.

Note that because of the way Rails reloads model classes, the plugin can sometimes bog down your development server. Set 
<tt>config.cache_classes = true</tt> in <tt>config/environments/development.rb</tt> to avoid this. 

== Reporting problems

The support forum is here[http://rubyforge.org/forum/forum.php?forum_id=16450].

Patches and contributions are very welcome. Please note that contributors are required to assign copyright for their 
additions to Cloudburst, LLC.

== Further resources

* http://blog.evanweaver.com/articles/2007/08/15/polymorphs-tutorial
* http://blog.evanweaver.com/articles/2007/02/22/polymorphs-25-total-insanity-branch
* http://blog.evanweaver.com/articles/2007/02/09/how-to-find-the-most-popular-tags
* http://blog.evanweaver.com/articles/2007/01/13/growing-up-your-acts_as_taggable
* http://blog.evanweaver.com/articles/2006/12/02/polymorphs-19
* http://blog.evanweaver.com/articles/2006/11/05/directed-double-polymorphic-associations
* http://blog.evanweaver.com/articles/2006/11/04/namespaced-model-support-in-has_many_polymorphs
* http://blog.evanweaver.com/articles/2006/09/26/sti-support-in-has_many_polymorphs
* http://blog.evanweaver.com/articles/2006/09/11/make-polymorphic-children-belong-to-only-one-parent