Implement Multiple Table Inheritance Into Your ActiveRecord Models
March 11th, 2012 // 10:05 pm @ matt
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.
Mark Molloy
1 year ago
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?
matt
1 year ago
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_subtypeoras_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!alonso jonnyjava
1 year ago
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 :departmentin the Employee classand
has_many :employees, :dependent => :destroyinside the Department class but that doesn’t work properly. It throughs an error like “NameError: uninitialized constant Employee”. Any clue about that?BR!
matt
12 months ago
Hi Alonso. Are you defining
Employeewithin a namespace? What happens when you utilize the:class_nameoption on your relationship?