Single Table Inheritance (STI) parent ActiveRecord .subclasses .descendants returns empty

Viewed 2126

I have a STI in place for 10 models inheriting from one ActiveRecord::Base model.

class Listing::Numeric < ActiveRecord::Base
end

class Listing::AverageDuration < Listing::Numeric
end

class Listing::TotalViews < Listing::Numeric
end

There are 10 such models those inherit from Listing::Numeric

In rails console, when I try for .descendants or .subclasses it returns an empty array.

Listing::Numeric.descendants
=> []

Listing::Numeric.subclasses
=> []

Ideally this should work.

Any ideas why its not returning the expected subclasses ?

5 Answers

Here is a solution for Rails 7. Originally sourced from RailsGuides, I made modifications to include results even if no data is stored in the table:

module StiPreload
  unless Rails.application.config.eager_load
    extend ActiveSupport::Concern

    included do
      cattr_accessor :preloaded, instance_accessor: false
    end

    class_methods do
      def descendants
        preload_sti unless preloaded
        super
      end

      def preload_sti
        types_from_db = base_class
                      .unscoped
                      .select(inheritance_column)
                      .distinct
                      .pluck(inheritance_column)
                      .compact

        (types_from_db.present? || types_from_file).each do |type|
          type.constantize
        end

        self.preloaded = true
      end

      def types_from_file
        Dir::each_child("#{Rails.root}/app/models").reduce([]) do |acc, filename|
          if filename =~ /^(#{base_class.to_s.split(/(?=[A-Z])/).first.downcase})_(\w+)_datum.rb$/
            acc << "#{$1.capitalize}#{$2.classify}"
          end
          acc
        end
      end
    end
  end
end

class YoursTruly < ApplicationRecord
  include StiPreload
end

YoursTruly.descendants # => [...]
Related