Search code examples
ruby-on-railsruby-on-rails-4activerecordsingle-table-inheritancesti

Single Table Inheritance - Additional Class Name


I have a situation in my Rails 4 app, where I have STI and want to customize the default queries by additional type.

Classes:

class Candidate < ApplicationRecord
end

class Candidate::Site < Candidate
end

Now, if I make query, I get results as follow:

> Candidate::Site.count
# SELECT COUNT(*) FROM "candidates" WHERE "candidates"."type" IN ('Candidate::Site')
=> 0

Now, in my situation, I want to add an additional type which query should look for every time. By leveraging the IN clause, my expected query to be fired is:

SELECT COUNT(*) FROM "candidates" WHERE "candidates"."type" IN ('Candidate::Site', 'Site')

Can someone help me out to control this IN clause here? Thanks in advance.


Solution

  • After detailed research and diving deep into the Rails STI source code, I found that my scenario will need to override the Rails' default STI. Following is what I needed to achieve the goal:

    
    class Candidate::Site
    
      # To enable renaming of Site to Candidate::Site
      # per ENG-9551, we need to:
      # 1. disable the 'type' column from using Rails' built-in STI
      self.inheritance_column = :_nonexistant_column_to_bypass_sti
    
      # 2. manually set the type column to Candidate::Site when we save,
      # so that new and edited records are saved as Candidate::Site
      before_save { self.type = 'Candidate::Site' }
    
      # 3. always report the type as a Candidate::Site
      def type
        'Candidate::Site'
      end
    
      # 4. and set a default scope which only reads type columns that are
      # 'Candidate::Site' or 'Site' so that STI
      # still appears to be the same as always
      default_scope { where(type: 'Candidate::Site').or(where(type: 'Site')) }
    
      ...
      ...
      ...
    end
    

    So, now any new record created with Candidate::Site.create will store type Candidate::Site while queries will use default scope and consider both the type, Candidate::Site as well as Site.