With single table inheritance you have a base model which inherits from ActiveRecord::Base, then one or more sub-classes, which inherit from the base model.
Single table inheritance is a software pattern described by Martin Fowler. Since (most) databases don't support inheritance, there is an issue when trying to map objects to database tables. This is known as the Object-relational impedance mismatch.
Rails uses the Single Table Inheritance pattern to solve this problem. The basic idea is that another field in the base database table is used to store the type of the object.
If you want to acurately model a domain then inheritance is going to be necessary at some point or another. So single table inheritance gives you this flexibility.
Just be careful when using single table inheritance. Since all the data from all the sub-types is include in one table, you can end up with a lot of "null"s scattered throughout the table. These ultimately increase the size of the table and you could end up with a scaling problem on your hands.
For instance, say you have an abstract object called "Employee" and several sub-classes called "FullTimeEmployee", "TempEmployee" and "StudentEmployee". This is shown in the class diagram below:
As you can see, the super-class (Employee) has two instance variables "Name" and "Salary". Further to this, each of the sub-types have an instance variable relating, specifically to them. These instance variables in the sub-classes are what cause the problems. If you have a look at the table below, all will become clear:
Type Name Salary Hours Duration University FullTimeEmployee Jim 10,000 37 null null TempEmployee John 15,000 null 5 null StudentEmployee Joe 20,000 null null Queens University, Belfast
As you can see, each type has nulls in the fields which don't apply to it. This problem is only compounded when extra objects are added. So, as I already mentioned, the scaling problems can be serious, so keep this in mind when modeling your application.
Using the "Employee" model above we first need to generate the Employee model:
ruby script/generate model employee name:string salary:string hours:string duration:string university:string type:string
Notice we have included a "type" field. This is used to store the type of object that the record applies to. Now migrate this into the database:
Now, in the app/models folder create a model file for each sub-class:
We can now test this out using the console:
>> FullTimeEmployee.create!(:name => "Jim", :salary => "10,000", :hours => "37") >> TempEmployee.create!(:name => "John", :salary => "15,000", :duration => "5") >> StudentEmployee.create!(:name => "Joe", :salary => "20,000", :university => "Queens University, Belfast")
Now, print the records, we just created, to screen:
>> y Employee.all
name: Jim updated_at: 2009-06-12 19:16:42 university: salary: "10,000" type: FullTimeEmployee id: "1" hours: "37" duration: created_at: 2009-06-12 19:16:42
name: John updated_at: 2009-06-12 19:19:50 university: salary: "15,000" type: TempEmployee id: "2" hours: duration: "5" created_at: 2009-06-12 19:19:50
name: Joe updated_at: 2009-06-12 19:19:59 university: Queens University, Belfast salary: "20,000" type: StudentEmployee id: "3" hours: duration: created_at: 2009-06-12 19:19:59
See how the type field has been updated for you?
Now, when we retrieve the employees, we want them to be of the appropriate type. Let's see if this works by getting all employees and calling the "class" method for each.
>> employees = Employee.all >> employees.each do |employee| ?> puts employee.class >> end
FullTimeEmployee TempEmployee StudentEmployee
So it works! Yah!
Single table inheritance is extremely useful for modeling more complex domains. However, it should be used with caution as it can lead to scaling problems.