Skip to content
This repository
Browse code

Reorganized initializers a bit to enable better hooks for common case…

…s without the need for Railtie. Specifically, the following hooks were added:

* before_configuration: this hook is run immediately after the Application class 
  comes into existence, but before the user has added any configuration. This is
  the appropriate place to set configuration for your plugin
* before_initialize: This is run after all of the user's configuration has completed,
  but before any initializers have begun (in other words, it runs right after
  config/environments/{development,production,test}.rb)
* after_initialize: This is run after all of the initializers have run. It is an
  appropriate place for forking in a preforking setup

Each of these hooks may be used via ActiveSupport.on_load(name) { }. In all these cases, the context inside the block will be the Application object. This means that for simple cases, you can use these hooks without needing to create a Railtie.
  • Loading branch information...
commit 9cfeefb637b603ce41d3019c8baa95ea984620d7 1 parent 458f571
Yehuda Katz authored May 15, 2010
17  activerecord/lib/active_record/railtie.rb
@@ -15,6 +15,12 @@ class Railtie < Rails::Railtie
15 15
     config.generators.orm :active_record, :migration => true,
16 16
                                           :timestamps => true
17 17
 
  18
+    config.app_middleware.insert_after "::ActionDispatch::Callbacks",
  19
+      "ActiveRecord::QueryCache"
  20
+
  21
+    config.app_middleware.insert_after "::ActionDispatch::Callbacks",
  22
+      "ActiveRecord::ConnectionAdapters::ConnectionManagement"
  23
+
18 24
     rake_tasks do
19 25
       load "active_record/railties/databases.rake"
20 26
     end
@@ -58,16 +64,9 @@ class Railtie < Rails::Railtie
58 64
       end
59 65
     end
60 66
 
61  
-    # Setup database middleware after initializers have run
62  
-    initializer "active_record.initialize_database_middleware", :after => "action_controller.set_configs" do |app|
63  
-      middleware = app.config.middleware
64  
-      middleware.insert_after "::ActionDispatch::Callbacks", ActiveRecord::QueryCache
65  
-      middleware.insert_after "::ActionDispatch::Callbacks", ActiveRecord::ConnectionAdapters::ConnectionManagement
66  
-    end
67  
-
68 67
     initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app|
69  
-      ActiveSupport.on_load(:active_record) do
70  
-        unless app.config.cache_classes
  68
+      unless app.config.cache_classes
  69
+        ActiveSupport.on_load(:active_record) do
71 70
           ActionDispatch::Callbacks.after do
72 71
             ActiveRecord::Base.reset_subclasses
73 72
             ActiveRecord::Base.clear_reloadable_connections!
18  activesupport/lib/active_support/lazy_load_hooks.rb
@@ -2,16 +2,26 @@ module ActiveSupport
2 2
   @load_hooks = Hash.new {|h,k| h[k] = [] }
3 3
   @loaded = {}
4 4
 
5  
-  def self.on_load(name, &block)
  5
+  def self.on_load(name, options = {}, &block)
6 6
     if base = @loaded[name]
7  
-      base.instance_eval(&block)
  7
+      execute_hook(base, options, block)
8 8
     else
9  
-      @load_hooks[name] << block
  9
+      @load_hooks[name] << [block, options]
  10
+    end
  11
+  end
  12
+
  13
+  def self.execute_hook(base, options, block)
  14
+    if options[:yield]
  15
+      block.call(base)
  16
+    else
  17
+      base.instance_eval(&block)
10 18
     end
11 19
   end
12 20
 
13 21
   def self.run_load_hooks(name, base = Object)
14  
-    @load_hooks[name].each { |hook| base.instance_eval(&hook) }
15 22
     @loaded[name] = base
  23
+    @load_hooks[name].each do |hook, options|
  24
+      execute_hook(base, options, hook)
  25
+    end
16 26
   end
17 27
 end
67  activesupport/test/lazy_load_hooks_test.rb
... ...
@@ -0,0 +1,67 @@
  1
+require 'abstract_unit'
  2
+
  3
+class LazyLoadHooksTest < ActiveSupport::TestCase
  4
+  def test_basic_hook
  5
+    i = 0
  6
+    ActiveSupport.on_load(:basic_hook) { i += 1 }
  7
+    ActiveSupport.run_load_hooks(:basic_hook)
  8
+    assert_equal 1, i
  9
+  end
  10
+
  11
+  def test_hook_registered_after_run
  12
+    i = 0
  13
+    ActiveSupport.run_load_hooks(:registered_after)
  14
+    assert_equal 0, i
  15
+    ActiveSupport.on_load(:registered_after) { i += 1 }
  16
+    assert_equal 1, i
  17
+  end
  18
+
  19
+  def test_hook_receives_a_context
  20
+    i = 0
  21
+    ActiveSupport.on_load(:contextual) { i += incr }
  22
+    assert_equal 0, i
  23
+    ActiveSupport.run_load_hooks(:contextual, FakeContext.new(2))
  24
+    assert_equal 2, i
  25
+  end
  26
+
  27
+  def test_hook_receives_a_context_afterward
  28
+    i = 0
  29
+    ActiveSupport.run_load_hooks(:contextual_after, FakeContext.new(2))
  30
+    assert_equal 0, i
  31
+    ActiveSupport.on_load(:contextual_after) { i += incr }
  32
+    assert_equal 2, i
  33
+  end
  34
+
  35
+  def test_hook_with_yield_true
  36
+    i = 0
  37
+    ActiveSupport.on_load(:contextual_yield, :yield => true) do |obj|
  38
+      i += obj.incr + incr_amt
  39
+    end
  40
+    assert_equal 0, i
  41
+    ActiveSupport.run_load_hooks(:contextual_yield, FakeContext.new(2))
  42
+    assert_equal 7, i
  43
+  end
  44
+
  45
+  def test_hook_with_yield_true_afterward
  46
+    i = 0
  47
+    ActiveSupport.run_load_hooks(:contextual_yield_after, FakeContext.new(2))
  48
+    assert_equal 0, i
  49
+    ActiveSupport.on_load(:contextual_yield_after, :yield => true) do |obj|
  50
+      i += obj.incr + incr_amt
  51
+    end
  52
+    assert_equal 7, i
  53
+  end
  54
+
  55
+private
  56
+
  57
+  def incr_amt
  58
+    5
  59
+  end
  60
+
  61
+  class FakeContext
  62
+    attr_reader :incr
  63
+    def initialize(incr)
  64
+      @incr = incr
  65
+    end
  66
+  end
  67
+end
2  railties/guides/source/initialization.textile
Source Rendered
@@ -2379,7 +2379,6 @@ Now that we've referenced that class, it will be required for us. You'll notice
2379 2379
 * initialize_subscriber
2380 2380
 * set_clear_dependencies_hook
2381 2381
 * initialize_dependency_mechanism
2382  
-* bootstrap_load_path
2383 2382
 
2384 2383
 These are all defined using the +initializer+ method:
2385 2384
 
@@ -2930,7 +2929,6 @@ With +@@autoloads+ being
2930 2929
 * initialize_subscriber
2931 2930
 * set_clear_dependencies_hook
2932 2931
 * initialize_dependency_mechanism
2933  
-* bootstrap_load_path
2934 2932
 
2935 2933
 h4. Active Support Initializers
2936 2934
 
10  railties/lib/rails/application.rb
@@ -12,7 +12,7 @@ module Rails
12 12
   # points to it.
13 13
   #
14 14
   # In other words, Rails::Application is Singleton and whenever you are accessing
15  
-  # Rails::Application.config or YourApplication::Application.config, you are actually 
  15
+  # Rails::Application.config or YourApplication::Application.config, you are actually
16 16
   # accessing YourApplication::Application.instance.config.
17 17
   #
18 18
   # == Initialization
@@ -40,7 +40,7 @@ module Rails
40 40
   #
41 41
   # The Application is also responsible for building the middleware stack and setting up
42 42
   # both application and engines metals.
43  
-  # 
  43
+  #
44 44
   class Application < Engine
45 45
     autoload :Bootstrap,      'rails/application/bootstrap'
46 46
     autoload :Configurable,   'rails/application/configurable'
@@ -69,6 +69,8 @@ def inherited(base)
69 69
         raise "You cannot have more than one Rails::Application" if Rails.application
70 70
         super
71 71
         Rails.application = base.instance
  72
+
  73
+        ActiveSupport.run_load_hooks(:before_configuration, base.instance)
72 74
       end
73 75
 
74 76
       def respond_to?(*args)
@@ -82,7 +84,7 @@ def method_missing(*args, &block)
82 84
       end
83 85
     end
84 86
 
85  
-    delegate :metal_loader, :to => :config
  87
+    delegate :middleware, :metal_loader, :to => :config
86 88
 
87 89
     def require_environment!
88 90
       environment = paths.config.environment.to_a.first
@@ -125,7 +127,7 @@ def load_generators
125 127
     end
126 128
 
127 129
     def app
128  
-      @app ||= middleware.build(routes)
  130
+      @app ||= config.middleware.build(routes)
129 131
     end
130 132
 
131 133
     def call(env)
8  railties/lib/rails/application/bootstrap.rb
@@ -10,7 +10,8 @@ module Bootstrap
10 10
         require environment if environment
11 11
       end
12 12
 
13  
-      initializer :load_all_active_support do
  13
+      initializer :load_active_support do
  14
+        require 'active_support/dependencies'
14 15
         require "active_support/all" unless config.active_support.bare
15 16
       end
16 17
 
@@ -18,7 +19,6 @@ module Bootstrap
18 19
       # Used by Passenger to ensure everything's loaded before forking and
19 20
       # to avoid autoload race conditions in JRuby.
20 21
       initializer :preload_frameworks do
21  
-        require 'active_support/dependencies'
22 22
         ActiveSupport::Autoload.eager_autoload! if config.preload_frameworks
23 23
       end
24 24
 
@@ -66,8 +66,8 @@ module Bootstrap
66 66
         ActiveSupport::Dependencies.mechanism = config.cache_classes ? :require : :load
67 67
       end
68 68
 
69  
-      initializer :bootstrap_load_path do
70  
-        # This is just an initializer used as hook so all load paths are loaded together
  69
+      initializer :bootstrap_hook do |app|
  70
+        ActiveSupport.run_load_hooks(:before_initialize, app)
71 71
       end
72 72
     end
73 73
   end
2  railties/lib/rails/application/configuration.rb
@@ -33,7 +33,7 @@ def encoding=(value)
33 33
       end
34 34
 
35 35
       def middleware
36  
-        @middleware ||= default_middleware_stack
  36
+        @middleware ||= app_middleware.merge_into(default_middleware_stack)
37 37
       end
38 38
 
39 39
       def metal_loader
6  railties/lib/rails/application/finisher.rb
@@ -35,10 +35,8 @@ module Finisher
35 35
         app
36 36
       end
37 37
 
38  
-      initializer :after_initialize do
39  
-        config.after_initialize_blocks.each do |block|
40  
-          block.call(self)
41  
-        end
  38
+      initializer :finisher_hook do |app|
  39
+        ActiveSupport.run_load_hooks(:after_initialize, app)
42 40
       end
43 41
 
44 42
       # Disable dependency loading during request cycle
15  railties/lib/rails/engine.rb
@@ -45,7 +45,7 @@ module Rails
45 45
   #       app.middleware.use MyEngine::Middleware
46 46
   #     end
47 47
   #   end
48  
-  # 
  48
+  #
49 49
   # == Paths
50 50
   #
51 51
   # Since Rails 3.0, both your Application and Engines do not have hardcoded paths.
@@ -125,7 +125,7 @@ def find_root_with_flag(flag, default=nil)
125 125
       end
126 126
     end
127 127
 
128  
-    delegate :middleware, :paths, :root, :to => :config
  128
+    delegate :paths, :root, :to => :config
129 129
 
130 130
     def load_tasks
131 131
       super
@@ -133,7 +133,7 @@ def load_tasks
133 133
     end
134 134
 
135 135
     # Add configured load paths to ruby load paths and remove duplicates.
136  
-    initializer :set_load_path, :before => :bootstrap_load_path do
  136
+    initializer :set_load_path, :before => :bootstrap_hook do
137 137
       config.load_paths.reverse_each do |path|
138 138
         $LOAD_PATH.unshift(path) if File.directory?(path)
139 139
       end
@@ -142,7 +142,10 @@ def load_tasks
142 142
 
143 143
     # Set the paths from which Rails will automatically load source files,
144 144
     # and the load_once paths.
145  
-    initializer :set_autoload_paths, :before => :bootstrap_load_path do |app|
  145
+    #
  146
+    # This needs to be an initializer, since it needs to run once
  147
+    # per engine and get the engine as a block parameter
  148
+    initializer :set_autoload_paths, :before => :bootstrap_hook do |app|
146 149
       ActiveSupport::Dependencies.load_paths.unshift(*config.load_paths)
147 150
 
148 151
       if reloadable?(app)
@@ -200,7 +203,9 @@ def load_tasks
200 203
       end
201 204
     end
202 205
 
203  
-    initializer :load_app_classes do |app|
  206
+    # This needs to be an initializer, since it needs to run once
  207
+    # per engine and get the engine as a block parameter
  208
+    initializer :load_app_classes, :before => :finisher_hook do |app|
204 209
       next if $rails_rake_task
205 210
 
206 211
       if app.config.cache_classes
48  railties/lib/rails/railtie/configuration.rb
@@ -3,10 +3,50 @@
3 3
 module Rails
4 4
   class Railtie
5 5
     class Configuration
  6
+      class MiddlewareStackProxy
  7
+        def initialize
  8
+          @operations = []
  9
+        end
  10
+
  11
+        def insert_before(*args, &block)
  12
+          @operations << [:insert_before, args, block]
  13
+        end
  14
+
  15
+        alias insert insert_before
  16
+
  17
+        def insert_after(*args, &block)
  18
+          @operations << [:insert_after, args, block]
  19
+        end
  20
+
  21
+        def swap(*args, &block)
  22
+          @operations << [:swap, args, block]
  23
+        end
  24
+
  25
+        def use(*args, &block)
  26
+          @operations << [:use, args, block]
  27
+        end
  28
+
  29
+        def merge_into(other)
  30
+          @operations.each do |operation, args, block|
  31
+            other.send(operation, *args, &block)
  32
+          end
  33
+          other
  34
+        end
  35
+      end
  36
+
6 37
       def initialize
7 38
         @@options ||= {}
8 39
       end
9 40
 
  41
+      # This allows you to modify the application's middlewares from Engines.
  42
+      #
  43
+      # All operations you run on the app_middleware will be replayed on the
  44
+      # application once it is defined and the default_middlewares are
  45
+      # created
  46
+      def app_middleware
  47
+        @@app_middleware ||= MiddlewareStackProxy.new
  48
+      end
  49
+
10 50
       # Holds generators configuration:
11 51
       #
12 52
       #   config.generators do |g|
@@ -28,12 +68,8 @@ def generators
28 68
         end
29 69
       end
30 70
 
31  
-      def after_initialize_blocks
32  
-        @@after_initialize_blocks ||= []
33  
-      end
34  
-
35  
-      def after_initialize(&blk)
36  
-        after_initialize_blocks << blk if blk
  71
+      def after_initialize(&block)
  72
+        ActiveSupport.on_load(:after_initialize, :yield => true, &block)
37 73
       end
38 74
 
39 75
       def to_prepare_blocks
13  railties/test/application/initializers/initializers_test.rb
@@ -28,19 +28,6 @@ def setup
28 28
       assert_equal "congratulations", $test_after_initialize_block2
29 29
     end
30 30
 
31  
-    test "after_initialize block works correctly when no block is passed" do
32  
-      add_to_config <<-RUBY
33  
-        config.root = "#{app_path}"
34  
-        config.after_initialize { $test_after_initialize_block1 = "success" }
35  
-        config.after_initialize # don't pass a block, this is what we're testing!
36  
-        config.after_initialize { $test_after_initialize_block2 = "congratulations" }
37  
-      RUBY
38  
-      require "#{app_path}/config/environment"
39  
-
40  
-      assert_equal "success", $test_after_initialize_block1
41  
-      assert_equal "congratulations", $test_after_initialize_block2
42  
-    end
43  
-
44 31
     test "after_initialize runs after frameworks have been initialized" do
45 32
       $activerecord_configurations = nil
46 33
       add_to_config <<-RUBY

0 notes on commit 9cfeefb

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