First some code. I use count for brevity but the bug affects any finder method.
The bug is in how type_condition works.
# necessary schema
create_table :roots do |t|
t.column :title, :string
t.column :type, :string
end
# root.rb
class Root < ActiveRecord::Base; end
# intermediary.rb
class Intermediary < Root; end
# leaf.rb
class Leaf < Intermediary; end
# Start a new console in development env
script/console
# Create some records
Root.create :title => 'root'
Intermediary.create :title => 'intermediary'
Leaf.create :title => 'leaf'
# case 1
reload!; nil
Leaf.count => 1
Intermediary.count => 2
Root.count => 3
# all fine here.
# case 2
reload!; nil
Root.count => 3
Intermediary.count => 1 <= WRONG
Leaf.count => 1
Intermediary.count => 2 <= Right, now it knows about Leaf.
So here's what's going on:
Root.count always returns the correct value because Root counts regardless of type. For the root model, all records in the table are instances of it.
Leaf.count always returns the correct value because it only counts records of type Leaf, it doesn't care about other models in its hierarchy.
The problem is with the Intermediary class. It has to find/count both records with type Intermediary and Leaf. In case 1, Leaf will have already been loaded, so it'll be counted in Intermediary's subclasses. That'll allow Intermediary to build the correct type_condition, and it'll find the right records.
In case 2, however, Leaf has not been loaded and Intermediary has no way to know about it, so its type_condition will only match records of type Intermediary, not Leaf. After we load Leaf, Intermediary starts finding/counting correctly, because now Leaf is listed in Intermediary's subclass list.
I think this is buggy behavior because, with the same records in the table, the same method call will return different sets of records depending on which classes have been loaded in the program up to this point.
I talked about this on #rails-contrib with hasmanyjosh, and one idea was to have Root query the table for the values in the type column, and then try to load the respective models. I've been playing with this idea for a day now, and I don't think I can make it work satisfactorily. I'd also prefer not to couple AR with the Dependencies mechanism.
Another idea I had was to break things and aim for Rails 2.0. This is my preferred route for now. My idea is to store the entire model hierarchy in the type column. So, for our records in the example, we'd have the following values in the type column:
Root/
Root/Intermediary/
Root/Intermediary/Leaf/
This makes selecting the appropriate records much easier and independent of which classes are currently loaded. For Intermediary, for example, just query for type LIKE 'Root/Intermediary/%'
I realize this breaks a highly relied upon feature, in order to fix a relatively obscure bug. Most people only use one level of inheritance in their apps. I hope to provide a migration to help people get on with this change. I'd appreciate some feedback on the feasibility of checking such a change in.
Right now I'll begin work on a plugin that implements the type column modification, so it can be used by people on 1.2.x. Later on I'll turn it into a patch for the trunk.