Rails STI and multi-level inheritance queries
Asked Answered
S

2

9

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?

Singularize answered 8/10, 2015 at 3:25 Comment(2)
please specify the inheritance types in each class.Erastianism
What do you mean, huan son?Singularize
F
5

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).

Faretheewell answered 25/4, 2017 at 10:45 Comment(1)
That's an interesting solution. I'll try that next time this comes up!Singularize
D
3

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
Destroy answered 8/10, 2015 at 4:46 Comment(1)
Nice thought! It seems to have worked in the dummy app I made to replicate the problem, but didn't fix all of my problems in my real app (which could be caused by other things). I'll mark as accepted later when I've got time to properly test. Thanks!Singularize

© 2022 - 2024 — McMap. All rights reserved.