Implementing STI for classes within a module

Single Table Inheritance (STI) as design pattern has been made popular by rails primarily because of the native support and also because it can be very helpful when used judiciously. You can read more about it over here. Now, assuming we have a fair enough idea of what STI is and how to use it, let’s try to understand a particular problem and it’s solution.

Let me explain the problem through an example. Consider the following code.

class Application < ActiveRecord::Base
end

class Server < Application
end

class Worker < Application
end

This is a very standard STI implementation. We will have a single Application table with type column and we can have custom APIs exposed on Server and Worker models. Easy enough.

There is a little caveat to it though. What if Server and Worker could also mean something else in our domain and not just an application. In such a case, modules come in handy wherein we wrap all such related entities into one namespace. This is how our implementation may change.

module Application
  class Base < ActiveRecord::Base
  # common code goes here
  end

  class Server < Base
  # Server specific implementation goes here
  end

  class Worker < Base
  # Worker specific implementation goes here
  end
end

Now, we just don’t refer to Server but we explicity call it out, saying, Application::Server. Much cleaner.

But, how does the STI implementation change in such a case? Unless specified otherwise, any model that inherits from ActiveRecord should have a corresponding table in the database with the same(class) name.

It would not really be wise to have a table with the name as base as it does not really map to any domain entity. What we can do instead is use one of ActiveRecord functionality of specifying the table name in the parent class to refer for STI. Checkout the code below.

module Application
  class Base < ActiveRecord::Base
    self.table_name = 'applications'
    # common code goes here
  end

  class Server < Base
    # Server specific implementation goes here
  end

  class Worker < Base
    # Worker specific implementation goes here
  end
end

Let’s move ahead.

Now, when we create a new Server entity, let’s say by doing, Server.create, it creates an entry in the applications table with the type Application::Server. Rails, by default stores the fully qualified class name as type in the STI table. We might not want this behaviour for a couple of reasons:

  • This is redundant information. We are already in the application table and hence apppending Application to the type doesn’t really makes sense.
  • Querying the table becomes difficult. Each of our queries will have to append Application:: to query on type from the application table

How can we avoid this? Rails has a pretty neat API to overcome this. You can override the store_full_sti_class method in the parent class(the class inheriting from ActiveRecord) and make it return false. This is how our final implementation looks like.

module Application
  class Base < ActiveRecord::Base
    self.table_name = 'applications'

    def self.store_full_sti_class
      false
    end

    # common code goes here
  end

  class Server < Base
    # Server specific implementation goes here
  end

  class Worker < Base
    # Worker specific implementation goes here
  end
end

Voila! With these two simple overrides, we can implement STI for classes wrapped in a module.

Thanks for reading!