A Rails plugin that adds multi-table inheritance support using Postgres inheritance.
Ruby
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
lib
test
.gitignore
LICENSE
README.rdoc
Rakefile
VERSION.yml
init.rb
multi_table_inheritance.gemspec

README.rdoc

MultiTableInheritance

A plugin that adds multi-table inheritance support to Rails using Postgres inheritance. For more information on Postgres inheritance, see www.postgresql.org/docs/8.3/static/ddl-inherit.html.

Usage:

Assuming you have a base table migration like this:

class CreatePetsTable < ActiveRecord::Migration
  def self.up
    create_table :pets, :inherits_from => :profiles do |t|
      t.string :name,    :limit => 64
      t.string :species, :limit => 64
      t.timestamps
    end
  end
end

You can create an inherited table using the :inherits_from option.

class CreateDogsTable < ActiveRecord::Migration
  def self.up
    create_table :dogs, :inherits_from => :pets do |t|
      t.string :breed, :limit => 64
    end
    ##
    # Executes the following in postgres:
    # CREATE TABLE dogs (
    #   "breed" character varying(64) DEFAULT NULL NULL
    # ) INHERITS (profiles)
  end
end

The inherited table has all the columns of the base table in addition to any fields added in the migration. You just need to add a model for the inherited table and use the multi_table_inheritance directive.

class Dog < Pet
  multi_table_inheritance
end

You can find inherited models through the base class, and they will be of the inherited type, just like with single-table inheritance. However, by default, when you find inherited models through the base class, all attributes added in just the inherited table will be missing. Single-table inheritance does not have this problem because all attributes exist in all classes in the hierarchy.

Pet.find(11)
=> #<Dog:0x19840f8 @attributes={"id"=>11, "name"=>"Daisy", "species"=>"Dog"}

Dog.find(11)
=> #<Dog:0x1938284 @attributes={"id"=>11, "name"=>"Daisy", "species"=>"Dog", "breed"=>"Pit/Lab Mix"}

If you would like inherited attributes to be added the inherited models when finding from the base class, use the :add_fields option.

class Dog < Pet
  multi_table_inheritance :add_fields => true
end

Pet.find(11)
=> #<Dog:0x1938284 @attributes={"id"=>11, "name"=>"Daisy", "species"=>"Dog", "breed"=>"Pit/Lab Mix"}

This may have performance implications for you, because it fetches the record again from the inherited table. However, if you are using a model caching system (e.g. github.com/ninjudd/record_cache), the impact should be minimal.

Install:

sudo gem install ninjudd-multi_table_inheritance -s http://gems.github.com

Compatibility:

Requires Rails 2.3 because multi_table_inheritance overrides the default_select method in ActiveRecord::Base. Or alternatively, you can apply the following patch to construct_finder_sql in activerecord/lib/active_record/base.rb:

+        def default_select(qualified)
+          if qualified
+            quoted_table_name + '.*'
+          else
+            '*'
+          end
+        end
+
         def construct_finder_sql(options)
           scope = scope(:find)
-          sql  = "SELECT #{options[:select] || (scope && scope[:select]) || (options[:joins] && quoted_table_name + '.*') || '*'} "
+          sql  = "SELECT #{options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))} "
           sql << "FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "

License:

Copyright © 2009 Justin Balthrop, Geni.com; Published under The MIT License, see LICENSE