Today I wanted to make a minimal environment for running rake tasks, background jobs, scripts with script/runner and so on.
I started with this “config/environments/rake.rb” file:
config.frameworks = [ :active_record ]
config.cache_classes = true
config.gems.shift # removes Haml, the first gem dependency in environment.rb
config.plugins = [ :masochism, :will_paginate, :validates_existence ]
As you can see, I’m loading only ActiveRecord and gems/plugins that go with it.
But, I had a number of problems with this.
First, the environment-specific configuration files (“environments/ENV.rb”) are being loaded after the frameworks have been loaded. I don’t know why is this, except for the fact that require_frameworks
method from Rails::Initializer uses silence_warnings
from ActiveSupport. So, my config.frameworks
override was useless.
Solution: I moved the require_frameworks
call to the start of Initializer#process
. Of course, silence_warnings
is not defined by that time so I defined a no-op method on Initializer with the same name.
Second, Initializer wants to load_application_classes
– in other words, preload everything in the app/ directory (for thread-safety). This is a disaster, because ActionMailer and ActionController are not loaded.
Solution: add a special-case to prevent it, like
def load_application_classes
super unless Rails.env.rake?
end
I probably could have just said config.cache_classes = false
instead. Rails then assumes this is a development environment and doesn’t preload.
To conclude, this was all solved by subclassing Rails::Initializer, but I think Rails should support this out of the box. I propose the following:
- A tweak I described to solve my first problem;
- A configuration entry that specifies whether the environment should be in thread-safe mode. If not, don’t bother with preloading, connection pools in ActiveRecord and such. Thoughts?