rails / spring

Rails application preloader
MIT License
2.8k stars 341 forks source link

Rails STI subclasses being "forgotten" when env auto-reloads #340

Open fbatista opened 10 years ago

fbatista commented 10 years ago

Consider the following structure:

class Section < ActiveRecord::Base
  belongs_to :sectionable, polymorphic: true
end

class MarketSection < Section
end

class MarketNotesSection < MarketSection
end

When doing a query for sectionables that have has_many :market_sections, as: :sectionable , on the first run after the environment is loaded, it correctly performs sections.type IN ('MarketSection', 'MarketNotesSection'), because i added an initializer to preload the classes in development, as suggested in http://www.alexreisner.com/code/single-table-inheritance-in-rails for example.

However, as soon as i edit and save a bit of code (a controller for example), the environment "forgets" about the subclass of marketsection and stops including it in the query.

I'd be happy to provide more details if needed.

basvanwesting commented 10 years ago

Same problem. The following sometimes helps (in 'config/initializers/reload.rb'), but not always. I'm on rails 4.1.5, spring 1.1.3 and ruby 1.9.3.

Rails.application.config.to_prepare do
  require ...sti_file_here...
end
starbugs commented 9 years ago

This happens to me as well. Does anyone have any idea what causes this?

basvanwesting commented 9 years ago

I forgot about this issue, but I have a working solution. The key is to use require_dependency and not require.

The reason this happens is the way ruby looks up the classes traversing the class/module nesting. If you are looking for B in scope A, it will first try A::B and then fallback to ::B. This behaviour is called before the rails autoloader, thus if A::B is not loaded (or unloaded by rails), ::B is found (which is what you don't want). The solution is to tell the Rails autoloader to reload A::B on each request in development/test.

  #config/initializers/reload.rb
  Rails.application.config.to_prepare do
    require_dependency 'a/b'
    require_dependency 'b'
  end

The safest approach however is to never reuse class names which also exist in a lesser nested scope. e.g. A::B & C::B are safe and never conflict, since ::B doesn't exist the rails autoloader kicks in loading the class on demand.

However, I care more about my naming than these loader specifics, so I mostly end up using the config.to_prepare approach.

lostapathy commented 6 years ago

This is apparently still an issue, but I think it's an issue in rails not spring. I ran into the issue in an app that did not use spring.

akaspick commented 5 years ago

@lostapathy This just bit me as well. The suggested to_prepare solution seemed to work for me.

jrochkind commented 5 years ago

Yeah, I think it is a general Rails issue, and is kind of sort of documented in the autoloading Guide. It is a gotcha with STI and dev-mode auto-loading.

https://guides.rubyonrails.org/autoloading_and_reloading_constants.html#autoloading-and-sti