Multiple table inheritance (MTI) with Rails

I’ve googled around and searched the forum in hope of finding great reading material about how to implement multiple table interitance (MIT) also reffered to some as class table inheritance (CTI). I wonder if you guys have any recommendation on this topic. What are your experiences? Which techniques/gems you use?

There’s a gem called active_record-acts_as that attempts to solve this but from what I’ve heard it tends to introduce more problems than it solves.

For others that might be interested in the same topic here are some of the most useful links I’ve managed to find.

My problem

In an app I’m currently working on I have couple of models that I’d like to all behave similar. I’d be making all sorts of events (only 3 are on the graph for the sake of simplicity).

I imagine I’d be able to do things like:

CampaignEvent.create customer: @customer, campaign_name: "Newsletter"
VisitEvent.create customer: @customer, visited_at: Date.today, location: @location
....

Currently though I’d have to manually create two object each time I want to create an event:

  • create Event object
  • create custom event object (e.g. VisitEvent) with details
  • assign custom event object to Event object

Most of these records will be created by background jobs (parsing data from APIs). In the administration part of the site the user will mostly be listing/filtering the data.

Any suggestions?

1 Like

Hi @lenart,

I’m sure there are other factors not listed here for sake of brevity that make you lean towards MTI. However just in case could this simpler modelling yield the results you need?

Could you instead just have the one Event model with the following associations

Event < AR
belongs_to :subject
belongs_to :target, polymorphic: true
end

Then when a purchase is created (assuming you have a model for this) you can create an event like so

Event.create(subject: current_user, target: @purchase)

Your purcahse model would look like this

Purchase < AR
has_many :events, as: :target 
end

If you want to store more context you can do that via serialized attribute on your Event model and call it something like meta

Event.create(subject: current_user, target: @purchase, meta: { product_name: ‘Star wars dvd’ })

With standard serialization it wont be searchable via sql queries. If you are using Posgres you can store json but I cant remember if json columns are searchable with Postgres. However the product name I assume would be on the Product record which the purchase record would links to no? So it is there if you need it through joins.

If multiple joins and performance is an issue perhaps ElasticSearch might be helpful here as you can index multiple models (or types as it ES calls them) and perform one search across all of them (and get the event ids back in one swoop).

If ES seems a bit heavy handed and you want to stick with SQL then in your search controller you’ll have to create joins where needed. Maybe in your search form have nested fields for each event type and check for their presence in the params back in the controller and then do a joins. You’d probably need a class to handle this.

Thanks for your feedback @robodisco! The solution that I’m currently playing around with is similar to what you suggested. I am using polymorphic associations and I’ll see if using AR callbacks will do the trick for though I currently don’t feel too confident with this approach. I decided to just pick a solution and go with it - I might come back with more questions later on :wink:

Thanks also for your suggestion about using ElasticSearch as storage engine - I’m currently not considering using it but it might come helpful somewhere down the road.

1 Like

I just want to give a short update. I’ve found that Sequel has a plugin for class table inheritance which I found after reading an article about Class table inheritance in Rails.

I’ve also read an article by Maxim where he attempted to do what I was trying to do but he later added a warning saying his approach should not be used in production. I’ve contacted him and here’s what he wrote:

… The idea is: be explicit. Just create a has_one/belongs_to relationship, don’t try to pretend that your multiple models can act like one model. It might feel good to build this whole MTI setup, but as your project grows you will begin to hate dealing with this abstraction, it won’t be intuitive to you or anyone else who might work with your code. It’ll quickly turn into technical debt, an awkward piece of “cleverness” that you always worry about.

Keep it simple and explicit, embrace the fact that it’s just multiple models.

After my research yielded little-to-no great articles about this topic the answer from Maxim gave me the confidence to move forward creating separate objects for my Event class and corresponding event details model(s) (NoteEvent, PurchaseEvent etc.).

Glad you figured it out and now you can definately use sql queries to perform your searches.

Actually I think this is the approach Thoughtbot use in the Upcase repo. If you look at their User class you’ll find it’s associated with a mentor model and another ‘person’ type model (cant remember off the top of my head.). I think over in Clearance’s issues one of the maintainers mentioned they often favor using this ‘has one’ association solution over the inheritance approach.