Skip to content
This repository
Browse code

Allow users to choose when to eager_load the application or not.

Previously, the eager load behavior was mostly coupled to
config.cache_classes, however this was suboptimal since in
some environments a developer may want to cache classes but
not necessarily load them all on boot (for example, test env).

This pull request also promotes the use of config.eager_load
set to true by default in production. In the majority of the
cases, this is the behavior you want since it will copy most
of your app into memory on boot (which was also the previous
behavior).

Finally, this fix a long standing Rails bug where it was
impossible to access a model in a rake task when Rails was
set as thread safe.
  • Loading branch information...
commit e6747d87f3a061d153215715d56acbb0be20191f 1 parent df1a5e4
José Valim authored August 01, 2012
10  railties/lib/rails/application.rb
@@ -83,6 +83,13 @@ def initialize
83 83
       @queue            = nil
84 84
     end
85 85
 
  86
+    # Eager load all dependencies before eager loading
  87
+    # the application.
  88
+    def eager_load!
  89
+      railties.each(&:eager_load!)
  90
+      super
  91
+    end
  92
+
86 93
     # Returns true if the application is initialized.
87 94
     def initialized?
88 95
       @initialized
@@ -216,8 +223,9 @@ def run_tasks_blocks(app) #:nodoc:
216 223
       railties.each { |r| r.run_tasks_blocks(app) }
217 224
       super
218 225
       require "rails/tasks"
  226
+      config = self.config
219 227
       task :environment do
220  
-        $rails_rake_task = true
  228
+        config.eager_load = false
221 229
         require_environment!
222 230
       end
223 231
     end
15  railties/lib/rails/application/bootstrap.rb
@@ -13,6 +13,20 @@ module Bootstrap
13 13
         require "active_support/all" unless config.active_support.bare
14 14
       end
15 15
 
  16
+      initializer :set_eager_load, :group => :all do
  17
+        if config.eager_load.nil?
  18
+          warn <<-INFO
  19
+config.eager_load is set to nil. Please update your config/environments file accordingly:
  20
+
  21
+  * development - set it to false
  22
+  * test - set it to false (unless you use a tool that preloads your test environment)
  23
+  * production - set it to true
  24
+
  25
+INFO
  26
+          config.eager_load = config.cache_classes
  27
+        end
  28
+      end
  29
+
16 30
       # Preload all frameworks specified by the Configuration#frameworks.
17 31
       # Used by Passenger to ensure everything's loaded before forking and
18 32
       # to avoid autoload race conditions in JRuby.
@@ -60,7 +74,6 @@ module Bootstrap
60 74
       end
61 75
 
62 76
       # Sets the dependency loading mechanism.
63  
-      # TODO: Remove files from the $" and always use require.
64 77
       initializer :initialize_dependency_mechanism, :group => :all do
65 78
         ActiveSupport::Dependencies.mechanism = config.cache_classes ? :require : :load
66 79
       end
6  railties/lib/rails/application/configuration.rb
@@ -7,7 +7,7 @@ class Application
7 7
     class Configuration < ::Rails::Engine::Configuration
8 8
       attr_accessor :allow_concurrency, :asset_host, :asset_path, :assets, :autoflush_log,
9 9
                     :cache_classes, :cache_store, :consider_all_requests_local, :console,
10  
-                    :dependency_loading, :exceptions_app, :file_watcher, :filter_parameters,
  10
+                    :eager_load, :exceptions_app, :file_watcher, :filter_parameters,
11 11
                     :force_ssl, :helpers_paths, :logger, :log_formatter, :log_tags,
12 12
                     :preload_frameworks, :railties_order, :relative_url_root, :secret_token,
13 13
                     :serve_static_assets, :ssl_options, :static_cache_control, :session_options,
@@ -24,7 +24,6 @@ def initialize(*)
24 24
         @consider_all_requests_local   = false
25 25
         @filter_parameters             = []
26 26
         @helpers_paths                 = []
27  
-        @dependency_loading            = true
28 27
         @serve_static_assets           = true
29 28
         @static_cache_control          = nil
30 29
         @force_ssl                     = false
@@ -45,6 +44,7 @@ def initialize(*)
45 44
         @log_formatter                 = ActiveSupport::Logger::SimpleFormatter.new
46 45
         @queue                         = Rails::Queueing::Queue
47 46
         @queue_consumer                = Rails::Queueing::ThreadedConsumer
  47
+        @eager_load                    = nil
48 48
 
49 49
         @assets = ActiveSupport::OrderedOptions.new
50 50
         @assets.enabled                  = false
@@ -98,7 +98,7 @@ def paths
98 98
       def threadsafe!
99 99
         @preload_frameworks = true
100 100
         @cache_classes = true
101  
-        @dependency_loading = false
  101
+        @eager_load = true
102 102
         @allow_concurrency = true
103 103
         self
104 104
       end
4  railties/lib/rails/application/finisher.rb
@@ -50,7 +50,7 @@ module Finisher
50 50
       end
51 51
 
52 52
       initializer :eager_load! do
53  
-        if config.cache_classes && !(defined?($rails_rake_task) && $rails_rake_task)
  53
+        if config.eager_load
54 54
           ActiveSupport.run_load_hooks(:before_eager_load, self)
55 55
           eager_load!
56 56
         end
@@ -91,7 +91,7 @@ module Finisher
91 91
 
92 92
       # Disable dependency loading during request cycle
93 93
       initializer :disable_dependency_loading do
94  
-        if config.cache_classes && !config.dependency_loading
  94
+        if config.eager_load
95 95
           ActiveSupport::Dependencies.unhook!
96 96
         end
97 97
       end
3  railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
@@ -6,6 +6,9 @@
6 6
   # since you don't have to restart the web server when you make code changes.
7 7
   config.cache_classes = false
8 8
 
  9
+  # Do not eager load code on boot.
  10
+  config.eager_load = false
  11
+
9 12
   # Show full error reports and disable caching.
10 13
   config.consider_all_requests_local       = true
11 14
   config.action_controller.perform_caching = false
6  railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -4,6 +4,12 @@
4 4
   # Code is not reloaded between requests.
5 5
   config.cache_classes = true
6 6
 
  7
+  # Eager load code on boot. This eager loads most of Rails and
  8
+  # your application in memory, allowing both thread web servers
  9
+  # and those relying on copy on write to perform better.
  10
+  # Rake tasks automatically ignore this option for performance.
  11
+  config.eager_load = true
  12
+
7 13
   # Full error reports are disabled and caching is turned on.
8 14
   config.consider_all_requests_local       = false
9 15
   config.action_controller.perform_caching = true
5  railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
@@ -7,6 +7,11 @@
7 7
   # and recreated between test runs. Don't rely on the data there!
8 8
   config.cache_classes = true
9 9
 
  10
+  # Do not eager load code on boot. This avoids loading your whole application
  11
+  # just for the purpose of running a single test. If you are using a tool that
  12
+  # preloads Rails for running tests, you may have to set it to true.
  13
+  config.eager_load = false
  14
+
10 15
   # Configure static asset server for tests with Cache-Control for performance.
11 16
   config.serve_static_assets = true
12 17
   config.static_cache_control = "public, max-age=3600"
24  railties/test/application/rake_test.rb
@@ -55,6 +55,30 @@ def test_initializers_are_executed_in_rake_tasks
55 55
       assert_match "Doing something...", output
56 56
     end
57 57
 
  58
+    def test_does_not_explode_when_accessing_a_model_with_eager_load
  59
+      add_to_config <<-RUBY
  60
+        config.eager_load = true
  61
+
  62
+        rake_tasks do
  63
+          task :do_nothing => :environment do
  64
+            Hello.new.world
  65
+          end
  66
+        end
  67
+      RUBY
  68
+
  69
+      app_file "app/models/hello.rb", <<-RUBY
  70
+      class Hello
  71
+        def world
  72
+          puts "Hello world"
  73
+        end
  74
+      end
  75
+      RUBY
  76
+
  77
+      output = Dir.chdir(app_path){ `rake do_nothing` }
  78
+      puts output
  79
+      assert_match "Hello world", output
  80
+    end
  81
+
58 82
     def test_code_statistics_sanity
59 83
       assert_match "Code LOC: 5     Test LOC: 0     Code to Test Ratio: 1:0.0",
60 84
         Dir.chdir(app_path){ `rake stats` }
2  railties/test/isolation/abstract_unit.rb
@@ -12,7 +12,6 @@
12 12
 require 'minitest/autorun'
13 13
 require 'active_support/test_case'
14 14
 
15  
-# TODO: Remove setting this magic constant
16 15
 RAILS_FRAMEWORK_ROOT = File.expand_path("#{File.dirname(__FILE__)}/../../..")
17 16
 
18 17
 # These files do not require any others and are needed
@@ -118,6 +117,7 @@ def build_app(options = {})
118 117
       end
119 118
 
120 119
       add_to_config <<-RUBY
  120
+        config.eager_load = false
121 121
         config.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
122 122
         config.session_store :cookie_store, :key => "_myapp_session"
123 123
         config.active_support.deprecation = :log

0 notes on commit e6747d8

Petteri Räty

Will ChangeLog entries be added in separate commits? I would guess that many people have made use of the global variable so we do want to give them proper notification that rails does not provide it any more to detect if you are running inside rake.

Martin Schürrer

I actually just stumbled upon this.

@josevalim Could we properly deprecate this? I mean it's a global variable I always expected it to be public API because of that.

Compare c3ca7ac

Please sign in to comment.
Something went wrong with that request. Please try again.