Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 375
Obversity Avatar asked Oct 08 '15 03:10

Obversity


People also ask

Does Ruby support multi level inheritance?

Ruby supports only single class inheritance, it does not support multiple class inheritance but it supports mixins.

How does the Single_table inheritance work?

Single table inheritance is a way to emulate object-oriented inheritance in a relational database. When mapping from a database table to an object in an object-oriented language, a field in the database identifies what class in the hierarchy the object belongs to.

What is single type inheritance in rails?

Single-table inheritance (STI) is the practice of storing multiple types of values in the same table, where each record includes a field indicating its type, and the table includes a column for every field of all the types it stores.


1 Answers

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

like image 184
Michael Trojanek Avatar answered Oct 05 '22 11:10

Michael Trojanek