Wednesday, June 11, 2008

I am a VIP


A problem I found is how Model class inheritance reflects in table relationship. When I saw the Single-Table Inheritance (STI) introduced by Martin Fowler a couple of years ago, I thought it was really stupid.

The whole idea of STI is simple: include all super/sub classes' attributes in one big fat single table and use an extra column (may be called 'type') to distinguish different type of classes.

But the default rails way is STI. :(

Now I have a Model class User and want to have another Model class called VIPUser which is a subclass of User. VIPUser has a new attribute called vip_number which User doesn't have.

OK, let's do the magic.

First, create a VIPUser Model

ruby script/generate model VIPModel

exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/vip_user.rb
create test/unit/vip_user_test.rb
create test/fixtures/vip_users.yml
exists db/migrate
create db/migrate/008_create_vip_users.rb


Second, edit the migration script for VIPUser

008_create_vip_users.rb
class CreateVipUser < ActiveRecord::Migration
def self.up
add_column :users, :vip_number, :integer, :default => 0
add_column :users, :type, :string
end

def self.down
remove_column :users, :vip_number
remove_column :users, :type
end
end


Notice that two new columns are created in the users table (which is for Model class User). One is the attribute of VIPUser, 'vip_number' and another is 'type' which is used to distinguish the type of classes.

If you've already had a column called 'type' or you don't like the name, you can use any other names and later declare it in VIPUser Model class.

vip_user.rb
set_inheritance_column 'object_type'


Third, make VIPUser a subclass of User

vip_user.rb
class VIPUser < User
...
end


That is it!

Now if you create a new VIPUser, you will see Rails automatically populate a string 'VIPUser' in type field.


>>VIPUser.new
VIPUser id: nil,
user_name: nil,
status: "active",
created_at: nil,
updated_at: nil,
vip_number: 0,
type: "VIPUser"

No comments: