Obversity
Obversity

Reputation: 597

Rails STI and multi-level inheritance queries

In my database I have a table people, and I'm using single table inheritance, with these classes:

class Person < ActiveRecord::Base
end

class Member < Person
end

class Business < Member
end

Demonstration of the problem

The queries it generates confuse me. What I want is for Member.all to return all Businesses as well as any other subtypes of Member. Which it does, but only if I've accessed the Business class recently. I assume it's because my classes aren't being cached in development mode (for obvious reasons), but it still seems like strange/buggy behaviour.

Is this a bug in rails? Or is it working as intended? In either case, can anyone think of a good fix for development purposes?

Upvotes: 9

Views: 2112

Answers (2)

Michael Trojanek
Michael Trojanek

Reputation: 1943

This is intentional behaviour—the official Rails guide on Autoloading and Reloading Constants explains it pretty well in the section on Autoloading and STI:

A way to ensure this works correctly regardless of the order of execution is to load the leaves of the tree by hand at the bottom of the file that defines the root class:

# app/models/polygon.rb
class Polygon < ApplicationRecord
end
require_dependency 'square'

Only the leaves that are at least grandchildren need to be loaded this way. Direct subclasses do not need to be preloaded. If the hierarchy is deeper, intermediate classes will be autoloaded recursively from the bottom because their constant will appear in the class definitions as superclass.

So in your case, this would mean putting an require_dependency "business" at the end of your Person class.

However, beware of circular dependencies which can possibly be avoided by using require instead of require_dependency (even though it may prohibit Rails from tracking and reloading your files when changes are made—after all, require_dependency is a Rails-internal method).

Upvotes: 5

Drenmi
Drenmi

Reputation: 8777

By default, Rails is not eager loading your classes in development. Try changing the following line in your config/environments/development.rb:

# Do not eager load code on boot.
config.eager_load = false

to:

# Do eager load code on boot!
config.eager_load = true

Upvotes: 3

Related Questions