Class Table Inheritance¶ ↑
Class Table Inheritance for ActiveRecord using updateable views.
More about the pattern on www.martinfowler.com/eaaCatalog/classTableInheritance.html. This gem messes very little with Rails inheritance mechanism. Instead it relies on updatable views in the database to represent classes in the inheritance chain. The approach was first suggested by John Wilger.
Database: PostgreSQL 8.1+ only. Patches for other DBMS are welcome. Note that you are not required to use updateable views, children relations can be tables (with some triggers involved) or materialized views.
gem install updateable_views_inheritance
rails generate updateable_views_inheritance:install && rake db:migrate
config.active_record.schema_format = :sql
In case you're using fixtures, don't forget to run
after every change to the class hierarchy. Otherwise tests may fail.
class CtiExample < ActiveRecord::Migration def self.up create_table :locomotives do |t| t.column :name, :string t.column :max_speed, :integer t.column :type, :string end create_child(:steam_locomotives, :parent => :locomotives) do |t| t.decimal :water_consumption, :precision => 6, :scale => 2 t.decimal :coal_consumption, :precision => 6, :scale => 2 end create_child(:electric_locomotives, :table => :raw_electric_locomotives, :parent => :locomotives) do |t| t.decimal :electricity_consumption, :precision => 6, :scale => 2 end end def self.down drop_child :steam_locomotives drop_child :electric_locomotives drop_table :locomotives end end
And the models:
class Locomotive end class SteamLocomotive < Locomotive self.table_name = :steam_locomotives end class ElectricLocomotive < Locomotive self.table_name = :electric_locomotives end
Note that models of children classes must specify table name explicitly.
Changing Columns in Underlying Tables¶ ↑
class RemoveColumnInParentTable < ActiveRecord::Migration def self.up remove_parent_and_children_views(:locomotives) remove_column(:locomotives, :max_speed) rename_column(:name, :title) rebuild_parent_and_children_views(:locomotives) end end
Renaming Underlying Tables¶ ↑
remove_parent_and_children_views(:old_name) rename_table(:old_name,:new_name) execute "UPDATE updateable_views_inheritance SET child_aggregate_view = 'new_name' WHERE child_aggregate_view = 'old_name'" execute "UPDATE updateable_views_inheritance SET parent_relation = 'new_name' WHERE parent_relation = 'old_name'" rebuild_parent_and_children_views(:new_name)
Removing Classes¶ ↑
Note that you should remove only leaf classes (i.e. those that do not have descendants). If you want to erase a whole chain or part of chain you have to remove first the leaves and then their ancestors. Use
drop_child(child_view) in migrations.
Compatibility with Single Table Inheritance¶ ↑
The approach of this gem is completely independent from Rails built-in Single Table Inheritance. STI and CLTI can safely be mixed in one inheritance chain.
Testing Your App¶ ↑
If you use fixtures, you must run
rake updateable_views_inheritance:fixture to generate fixture for the updateable_views_inheritance table after you add/remove classes from the hierarchy or change underlying table or view names. Without it primary key sequence for inheritors' tables won't be bumped to the max and it might not be possible to save objects! If you don't use fixtures for the classes in the hierarchy you don't need to do that.
This gem re-enables referential integrity on fixture loading. This means that
may fail when there are foreign key constraints on tables. To fix this, explicitly declare fixture load order in
fixtures :roots, :trunks, :leafs, ...
for all fixtures you want to load.
Gem Development & Testing¶ ↑
The gem has a comprehensive test suite. In order to run it, your user must be a superuser in PostgreSQL. If this is not the case, run
createuser -s pesho (assuming your Unix account is