A different way to do polymorphic associations

I did not like the way that Rails did polymorphic associations. I found it impossible to add constraints into the database itself to make sure that things were hooked up like they were suppose to be. And doing the validation of all the if's, and's, and but's in Rails appeared to me would lead to very expensive validations.

I also did not like the concept behind single table inheritance. I find that whole concept extremely lame.

Mostly from ideas I got from the Postgres mailing list, I implemented another way to do polymorphic associations. It seems to be working for me. I thought I would share it. There are definitely some rough edges but those are not getting in my way right now. I've managed to tip toe around and get this to work with no modifications to Rails and only a small amount of work (once the tricks are figured out).

The polymophic base class in this example I call ItemBase (with a table of item_bases). The migration looks like this:

class CreateItemBases < ActiveRecord::Migration   def self.up     create_table :item_bases, :id => false do |t|       t.integer :item_id, :null => false       t.string :item_type, :null => false       t.timestamps     end     execute "ALTER TABLE item_bases ADD CONSTRAINT fk_item_bases_item_type              FOREIGN KEY (item_type) REFERENCES item_types(class_name)"     execute "CREATE OR REPLACE FUNCTION item_id_test(item_id INTEGER, item_type TEXT)                  RETURNS BOOLEAN AS $$                  DECLARE                      tn TEXT;                      qry TEXT;

                 BEGIN                      SELECT INTO tn table_name FROM item_types                          WHERE class_name = item_type;                      IF NOT FOUND THEN                          RETURN FALSE;                      END IF;                      qry = 'SELECT item_id FROM ' || quote_ident(tn)

' AS tn ' ||

                           'WHERE tn.item_id = ' || item_id::text || ';';                      EXECUTE qry;                      IF NOT FOUND THEN                          RETURN FALSE;                      END IF;                      RETURN TRUE;                  END;              $$ LANGUAGE plpgsql;"     execute "ALTER TABLE item_bases ADD CONSTRAINT ck_item_bases_item_id              CHECK (item_id_test(item_id, item_type))"     execute "ALTER TABLE item_bases ADD CONSTRAINT key_item_id              UNIQUE (item_id)"     execute "ALTER TABLE item_bases ADD CONSTRAINT key_item_tuple              UNIQUE (item_id, item_type)"     execute "CREATE SEQUENCE item_bases_item_id_seq"   end

  def self.down     drop_table :item_bases     execute "DROP SEQUENCE item_bases_item_id_seq"     execute "DROP FUNCTION item_id_test(INTEGER, TEXT)"   end end

The model looks like this:

class ItemBase < ActiveRecord::Base   set_primary_key "item_id"   belongs_to :item, :polymorphic => true

  def id     item_id   end end

In this example, the subclasses can be companies, people, etc. The migration to create the companies table is:

class CreateCompanies < ActiveRecord::Migration   MY_CLASS_NAME = "Company"   MY_TABLE_NAME = "companies"

  def self.up     create_table MY_TABLE_NAME, :id => false do |t|       t.integer :item_id, :null => false       t.string :item_type, :null => false, :default => MY_CLASS_NAME       t.timestamps