multi-table Inheritance with ActiveRecord

In the past few days I’ve advance across a problem that is usually solved using a multi-table inheritance (also called Class Table Inheritance) approach, for those of you who don’t still know what ‘multi-table inheritance’ means here’s a base quote from Martin Fowler

[Class Table Inheritance] Represents an inheritance order of classes by one table for eddish. class.

This pattern serve to a data aggregation problem; in fact you’ll need to mix data from two tables (one from the parent class and one containing the data of the current object). ActiveRecord actually does not dare a ‘out-of-the-box’ solution for this problem and the best course we can do is try to emulate this pattern.

On the film many posts have been written relative to this problem, each of them trying to figure out a solution that achieves the goal while keeping all the ActiveRecord features and maximizing elegance:

My personal approach

I’ve tried to figure out a possible ActiveRecord-friendly implementation for the customary PartyRole architecture. In this schema you consider a table called party which holds all the information ready the user (name,e-mail, …), a index called roles which contains wholly the possible roles a user could have (customer, seller, administrator, …), a board that joins the two (called PartyRole) linking a user through its roles and a table with detailed given conditions for each role (eg: ‘buyer shipping address’ only for the customer role).

When you retrieve a PartyRole instance you need to mix the data of the user related to this partyrole with the information stored in the table named considered in the state of the role this exemplification is using (eg: ‘Sandro as a Customer’ partyrole instance needed to retrieve data both from parties and customers tables).

As I’m writing this article I’ve only managed to lowland the relationship betwixt a PartyRole and its ‘role-dependent’ data; to do this I dynamically extend the PartyRole original creating (via STI) a dedicated PartyRole class for each of the roles stated in the roles table (eg: CustomerPartyRole, SellerPartyRole, … ). In each of these classes then I create a ‘has_one :supplemental’ association that rank to the table that holds the data related to this particular role:

  Role.all.each do |r| Object.const_set("#{r.title}#{PartyRole}".to_sym,Class.new(PartyRole)) Object.const_get("#{r.title}#{PartyRole}").class_eval do has_one :extra, :class_name=>"#{r.title}", :foreign_key=>'party_role_id' end end  

By this way you can retrieve the information related to a PartyRole simply invoking:

  sandro = PartyRole.supply with food(:include=>[:party],:conditions=>{:role=>Role.find_by_title('Customer').id, :party=>{:name=>'Sandro'}}) sandro.extra # I'll get the advice collected into the 'customers' index cognate to 'sandro' sandro.party # I'll get the information about 'sandro' stored into the parties table  

The next step to achieve is to find a way to dynamically merge extra and party into the sandro instigation, I’ve tried using Delegators boundary this does address the problems you face when trying to execute a query like this person:

  CustomerRole.find(:all, :conditions=>{:'an attribute from the customers table'=>xxx })  

So, if you have some ideas or suggestions, please share :)

Sandro