Disabling eager loading in application.rb prevents autoloading #3148

Closed
jrafanie opened this Issue Sep 27, 2011 · 9 comments

4 participants

@jrafanie

Using rails 3.1.1rc1, creating a new application, and disabling the eager loading via config.eager_load_paths = [] in application.rb effectively disables autoloading of models and most likely everything else.

I can workaround it by setting the autoload_paths to the eager_load_paths prior to disabling eager loading:
config.autoload_paths = config.eager_load_paths.dup
config.eager_load_paths = []

Full details here: https://gist.github.com/1245456

@josevalim
Ruby on Rails member

That's expected. You don't have anything in autoload paths by default. all your models, controllers are actually eager_load_paths, it just happens they are not eager loaded when you are in development for performance.

@josevalim josevalim closed this Oct 1, 2011
@josevalim josevalim reopened this Oct 1, 2011
@josevalim josevalim was assigned Oct 1, 2011
@josevalim josevalim closed this Oct 1, 2011
@jrafanie

Thanks @josevalim, but I don't understand. Autoloading of the User class, from my gist, works in both production and development rails console with eager_load_paths as the default app directories. When I set eager_load_paths to [], both production and development fail to autoload User in console. Therefore, it appears that the environment does not matter.

My issue occurs because I would like to autoload classes on first use but not eager load in any environment.

@Fryguy

To add to @jrafanie's comment, a vanilla Rails app, where I don't set anything in autoload_paths nor eager_load_paths will autoload any model / controller / helper, etc in both production and development just by accessing the class name. If I set eager_load_paths to [], then I am completely unable to autoload anything in both production and development. My expectation would be that autoloading would still work in either mode, regardless.

@josevalim
Ruby on Rails member

Here is the thing: your models/controller/helpers are not autoloaded in production. They are eager loaded at the moment the server starts. You can verify this by adding "puts :OMG" in a model body and check when OMG is printed. It will be printed when the server starts, not the first time you refer to your model. So at the moment you clear up eager_load_paths, they won't be available at all.

@josevalim
Ruby on Rails member

Also, notice this line:

https://github.com/rails/rails/blob/master/railties/lib/rails/engine.rb#L621

It says that all paths that are autoloaded are: the registered autoloaded paths, the autoload once paths and the eager load paths. As eager load does not happen in development, models/controllers/helpers fallback to being autoloaded.

@jrafanie

Thanks @josevalim for the explanation. My confusion comes from a change in behavior from rails 2.3.x to 3-3.1. Note: I didn't try rails 3.0 so I don't know when this was changed. In 2.3.x, I didn't need to explicitly add the directories in app to the load_paths even though I was resetting eager_load_paths to []. In other words, I could use Rails autoloading in dev/production/test even though nothing was being eager loaded.

Without looking at the code more in depth, I'm not sure why eager_load_paths are used as the default autoload_paths if none are provided. I can use my workaround to grab the eager_load_paths before resetting it but was hoping to use a cleaner solution.

@Fryguy

@josevalim said:

As eager load does not happen in development, models/controllers/helpers fallback to being autoloaded.

But that is not what is happening. There is no fallback, all you get is "NameError: uninitialized constant User".

@trevor

@Fryguy

it sounds like you're putting code in application.rb that would work in rails 2, but in rails 3 needs to be moved to it's own file in config/initializers/whatever.rb

@Fryguy

@trevor Not sure what you mean. I guess setting config.eager_load_paths could be done in a config/initializers, but typically those values are set in application.rb. In fact, the generated comments in application.rb explicitly tell you do that there for autoload_paths.

The problem is that there is an unclear, undocumented, internal coupling between autoload_paths, eager_load_paths, and your current Rails environment, when there exists situations where you may want those to be decoupled.

Basically, the way I understand it is this:
config.autoload_paths is never actually set to anything by default, even though the documentation in application.rb says that it defaults to certain directories. What actually happens is that config.eager_load_paths is set behind the scenes, and then it just works out that when you + them together it seems the code is working as expected. See https://github.com/rails/rails/blob/master/railties/lib/rails/engine.rb#L650

  • In production, rails make an (IMO, wrong) assumption that everything will be eager loaded, so it uses that combined Array for eager loading only, and since everything is eager loaded, there's no need for any autoloading.
  • In development, rails make an assumption that nothing is eager loaded, so it uses that combined Array for autoloading.

Again, there is a lot of coupling between these 3 things, with a lot of assumptions under the covers.

EDIT: This was all as of Rails 3.1.1. I have not tried with any latest changes to master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment