Search code examples
ruby-on-railsruby-on-rails-4inheritanceactiverecordsti

Using STI, the first read for an interim subclass is failing but subsequent reads work


I have Rails 4.2 model using STI that is failing on the first attempt to read the record but succeeds on later cases. The record is of type KbtPrimary which inherits from KbTagged which inherits from Keybox. I need to search on KbTagged but the record isn't found on the first read to KbTagged. It is found if the first read is either Keybox or KbtPrimary.

Simplified, the code looks like this:

class Keybox < ActiveRecord::Base
  belongs_to :company
  acts_as_tenant :company
end
class KbTagged < Keybox; end
class KbtPrimary < KbTagged; end
box = KbTagged.where(name: 'Automated').first
puts box.blank? ? "Box:nil;" : "Box:#{box.name};"
box = Keybox.where(name: 'Automated').first
puts box.blank? ? "Box:nil;" : "Box:#{box.name};"
box = KbTagged.where(name: 'Automated').first
puts box.blank? ? "Box:nil;" : "Box:#{box.name};"

And the output is this:

Box:nil;
Box:Automated;
Box:Automated;

If KbtPrimary or Keybox is used first, the output is correct:

box = KbtPrimary.where(name: 'Automated').first
puts box.blank? ? "Box:nil;" : "Box:#{box.name};"
box = Keybox.where(name: 'Automated').first
puts box.blank? ? "Box:nil;" : "Box:#{box.name};"
box = KbtPrimary.where(name: 'Automated').first
puts box.blank? ? "Box:nil;" : "Box:#{box.name};"

Outputs:

Box:Automated;
Box:Automated;
Box:Automated;

SQL for the first failing read:

"SELECT "keyboxes".* FROM "keyboxes" WHERE "keyboxes"."type" IN ('KbTagged') AND "keyboxes"."company_id" = 2 AND "keyboxes"."name" = 'Automated'"

FURTHER EDIT WITH MORE SQL:

class Keybox < ActiveRecord::Base
  belongs_to :company
  acts_as_tenant :company
end
class KbTagged < Keybox; end
class KbtPrimary < KbTagged; end
puts KbTagged.where(name: 'Automated').to_sql
box = KbTagged.where(name: 'Automated').first
puts box.blank? ? "Box:nil;" : "Box:#{box.name};"
puts Keybox.where(name: 'Automated').to_sql
box = Keybox.where(name: 'Automated').first
puts box.blank? ? "Box:nil;" : "Box:#{box.name};"
puts KbTagged.where(name: 'Automated').to_sql
box = KbTagged.where(name: 'Automated').first
puts box.blank? ? "Box:nil;" : "Box:#{box.name};"

SELECT "keyboxes".* FROM "keyboxes" WHERE "keyboxes"."type" IN ('KbTagged') AND "keyboxes"."company_id" = 2 AND "keyboxes"."name" = 'Automated'
Box:nil;
SELECT "keyboxes".* FROM "keyboxes" WHERE "keyboxes"."company_id" = 2 AND "keyboxes"."name" = 'Automated'
Box:Automated;
SELECT "keyboxes".* FROM "keyboxes" WHERE "keyboxes"."type" IN ('KbTagged', 'KbtPrimary') AND "keyboxes"."company_id" = 2 AND "keyboxes"."name" = 'Automated'
Box:Automated;

Solution

  • The issue is in config/environments/development.rb with the setting:

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

    Setting this true causes the read to work. However, this isn't really beneficial in a development environment. I am continuing to look for a more optimal solution if you have one.

    EDIT: I am currently issuing a require for the STI classes within app/config/initializers to get this done instead of forcing eager_load throughout the application. At this point, I believe that the impact will be that those classes will not be updated dynamically and will require a server restart to be implemented. I did note that there is a gem that might help which is RequireReloader.

    UPDATE: RequireReloader is working well for me on this. It does not need the initializer to preload the classes.