Implement Multiple Table Inheritance Into Your ActiveRecord Models

This past week, I released my first ever Ruby gem: multiple_table_inheritance. Multiple Table Inheritance is an ActiveRecord plugin designed for Rails 3.0+ designed to make table-level inheritance easier than ever.

Imagine you have an application that needs to maintain a list of employees. You’ll probably start out with a few columns including first name, last name, address, city, state, phone number, social security number, salary, and so on. However, as you start to work on defining the structure, you might find that certain roles need to track pieces of information that other roles don’t. For example, a manager might have a yearly bonus. Similarly, a programmer might have a list of languages they’re familiar with.

With the Multiple Table Inheritance gem, you’ll be able to implement this concept easily. All that is required is some special treatment with your migrations, and a method call in each ActiveRecord model that extends or inherits from another model. For example:

class Employee < ActiveRecord::Base
  acts_as_superclass
  attr_accessible :first_name, :last_name
  validates :first_name, :presence => true
  validates :last_name, :presence => true
  validates :salary, :presence => true, :numericality => { :min => 0 }
end
 
class Programmer < ActiveRecord::Base
  inherits_from :employee
  has_many :known_languages
  has_many :languages, :through => :known_languages, :dependent => :destroy
end
 
class Manager < ActiveRecord::Base
  inherits_from :employee
  validates :bonus, :presence => true, :numericality => true
end

Now, whenever you perform any kind of find on your Employee model, you’ll receive instances of Programmer and Manager as a result.

Employee.first
# yields: <Programmer employee_id: 1>
#     or: <Manager: employee_id: 1 bonus: 5000>

It even works with named scoped.

Employee.where(['created_at >= ?', 3.weeks.ago]).limit(5)
# yields: [<Programmer employee_id: 1>,
#          <Programmer employee_id: 2>,
#          <Manager employee_id: 5 bonus: 4500>,
#          <Programmer employee_id: 12>,
#          <Manager employee_id: 14 bonus: 3500>]

To get started, check out the Multiple Table Inheritance homepage on Github. Please keep in mind that it’s a work in progress. Feel free to submit a new issue or create a pull request if you encounter any problems, fix an issue, or add a new feature.

4 thoughts on “Implement Multiple Table Inheritance Into Your ActiveRecord Models

  • Matt, this looks pretty amazing. I’m starting the modeling for a Rails project where a part of the type hierachy — products — promises to be both deep and wide. Do you have working examples? And, do you know whether using multiple_table_inheritance is compatible with other popular Rails functionality, such as ActiveAdmin and Formtastic?

    • Thanks for commenting, Mark. I’m currently using this in the development of an advertising engine on another website I operate, and I’ve integrated it directly with both ActiveAdmin and Formtastic. One thing worth noting though is that I’ve encountered my gem does interfere with some built-in ActiveRecord features, such as counter caches. See the issue I logged on Github for reference.

      Given that, I’m considering modifying the gem a bit to make it a little less “magic” so that it would be necessary to call methods like as_subtype or as_supertype. However, I’ve yet to find time between my job and raising an infant. I’m definitely open to feedback and code changes on Github!

  • Hi!
    I’m using your gem. Right now I’m having an issue. Following your example here, let’s say that I have another class called Department which has many Employees and I want to delete all the employees whom belongs to a department when I delete that department assuming that one employee only belongs to one department.

    Basically that would be something like…

    belongs_to :department in the Employee class

    and

    has_many :employees, :dependent => :destroy inside the Department class but that doesn’t work properly. It throughs an error like “NameError: uninitialized constant Employee”. Any clue about that?

    BR!

    • Hi Alonso. Are you defining Employee within a namespace? What happens when you utilize the :class_name option on your relationship?

Leave a Reply

Your email address will not be published. Required fields are marked *