According to Wikipedia STI is
Single table inheritance is a way to emulate object-oriented inheritance in a relational database. When mapping from a database table to an object in an object-oriented language, a field in the database identifies what class in the hierarchy the object belongs to. In Ruby on Rails the field in the table called ‘type’ identifies the name of the class.
I hope that explains you what STI is, so lets see how can we implement this in our Rails Application. Let’s consider a scenario where we have a user, the user can be multiple types depending on the access rights, like “Owner”, “Admin”, “Guest”.
First Step : Create a class with the name of a specific type(For instance : Owner)
Create a new Ruby class file in your rails app/models directory and give it the name of one of the type/s e.g. Owner.rb
And similarly create classes for
Second Step : Let these classes extend the User class
class Owner < User end
Third Step : Add a type field to the User Model
This type field will store the type of user. ie. Owner, Admin or Guest. You can add the field as follows:
class CreateUsers < ActiveRecord::Migration def self.up create_table :users do |t| t.string :name t.string :email t.string :password t.string :type end def self.down drop_table :user end end
Fourth Step : Add the following code in the User Model
class << self def new_with_cast(*a,&b) if (h = a.first).is_a? Hash and (type = h[:type] || h['type']) and (klass = type.constantize) != self raise "Error!!" unless klass < self # klass should be a descendant of User return klass.new(h, &b) end new_without_cast(*a, &b) end alias_method_chain :new, :cast end
What we do here basically is, we are checking
- Are we getting some value in the type field from the user submitted values
- If we are getting some value we constantize the string and then we check whether the recieved constant is one of the descendant of User class. If not then we raise an Exception.
- If the constant is one of the descendant of User class [‘Owner’,’Admin’,’Guest’], then the User becomes of that type.
Fifth Step : Changes in
In new and edit page of User add a select field which will determine the type of User
<p> <%= f.label :type %><br /> <%= f.select :type, ["Owner", "Admin", "Guest"] %> </p>
Last Step : We have done it. Time to test.
That is it. We have successfully implemented the functionality of Single Table Inheritance. Lets take a look how things go.
User.create(:name => "Rohit", :email => "firstname.lastname@example.org", :password => "test123456" ,:type => "Owner") #This line will create a record in User model with type Owner. @user = User.find_by_name("Rohit") #This will give an object of type Owner, because we have set the type as Owner.
Everything seems to be working fine.
Thanks for reading the post. any suggestions are most welcome.