提交 9cfeefb6 编写于 作者: W wycats

Reorganized initializers a bit to enable better hooks for common cases without...

Reorganized initializers a bit to enable better hooks for common cases 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.
上级 458f5712
...@@ -15,6 +15,12 @@ class Railtie < Rails::Railtie ...@@ -15,6 +15,12 @@ class Railtie < Rails::Railtie
config.generators.orm :active_record, :migration => true, config.generators.orm :active_record, :migration => true,
:timestamps => true :timestamps => true
config.app_middleware.insert_after "::ActionDispatch::Callbacks",
"ActiveRecord::QueryCache"
config.app_middleware.insert_after "::ActionDispatch::Callbacks",
"ActiveRecord::ConnectionAdapters::ConnectionManagement"
rake_tasks do rake_tasks do
load "active_record/railties/databases.rake" load "active_record/railties/databases.rake"
end end
...@@ -58,16 +64,9 @@ class Railtie < Rails::Railtie ...@@ -58,16 +64,9 @@ class Railtie < Rails::Railtie
end end
end end
# Setup database middleware after initializers have run
initializer "active_record.initialize_database_middleware", :after => "action_controller.set_configs" do |app|
middleware = app.config.middleware
middleware.insert_after "::ActionDispatch::Callbacks", ActiveRecord::QueryCache
middleware.insert_after "::ActionDispatch::Callbacks", ActiveRecord::ConnectionAdapters::ConnectionManagement
end
initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app| initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app|
ActiveSupport.on_load(:active_record) do
unless app.config.cache_classes unless app.config.cache_classes
ActiveSupport.on_load(:active_record) do
ActionDispatch::Callbacks.after do ActionDispatch::Callbacks.after do
ActiveRecord::Base.reset_subclasses ActiveRecord::Base.reset_subclasses
ActiveRecord::Base.clear_reloadable_connections! ActiveRecord::Base.clear_reloadable_connections!
......
...@@ -2,16 +2,26 @@ module ActiveSupport ...@@ -2,16 +2,26 @@ module ActiveSupport
@load_hooks = Hash.new {|h,k| h[k] = [] } @load_hooks = Hash.new {|h,k| h[k] = [] }
@loaded = {} @loaded = {}
def self.on_load(name, &block) def self.on_load(name, options = {}, &block)
if base = @loaded[name] if base = @loaded[name]
base.instance_eval(&block) execute_hook(base, options, block)
else else
@load_hooks[name] << block @load_hooks[name] << [block, options]
end
end
def self.execute_hook(base, options, block)
if options[:yield]
block.call(base)
else
base.instance_eval(&block)
end end
end end
def self.run_load_hooks(name, base = Object) def self.run_load_hooks(name, base = Object)
@load_hooks[name].each { |hook| base.instance_eval(&hook) }
@loaded[name] = base @loaded[name] = base
@load_hooks[name].each do |hook, options|
execute_hook(base, options, hook)
end
end end
end end
\ No newline at end of file
require 'abstract_unit'
class LazyLoadHooksTest < ActiveSupport::TestCase
def test_basic_hook
i = 0
ActiveSupport.on_load(:basic_hook) { i += 1 }
ActiveSupport.run_load_hooks(:basic_hook)
assert_equal 1, i
end
def test_hook_registered_after_run
i = 0
ActiveSupport.run_load_hooks(:registered_after)
assert_equal 0, i
ActiveSupport.on_load(:registered_after) { i += 1 }
assert_equal 1, i
end
def test_hook_receives_a_context
i = 0
ActiveSupport.on_load(:contextual) { i += incr }
assert_equal 0, i
ActiveSupport.run_load_hooks(:contextual, FakeContext.new(2))
assert_equal 2, i
end
def test_hook_receives_a_context_afterward
i = 0
ActiveSupport.run_load_hooks(:contextual_after, FakeContext.new(2))
assert_equal 0, i
ActiveSupport.on_load(:contextual_after) { i += incr }
assert_equal 2, i
end
def test_hook_with_yield_true
i = 0
ActiveSupport.on_load(:contextual_yield, :yield => true) do |obj|
i += obj.incr + incr_amt
end
assert_equal 0, i
ActiveSupport.run_load_hooks(:contextual_yield, FakeContext.new(2))
assert_equal 7, i
end
def test_hook_with_yield_true_afterward
i = 0
ActiveSupport.run_load_hooks(:contextual_yield_after, FakeContext.new(2))
assert_equal 0, i
ActiveSupport.on_load(:contextual_yield_after, :yield => true) do |obj|
i += obj.incr + incr_amt
end
assert_equal 7, i
end
private
def incr_amt
5
end
class FakeContext
attr_reader :incr
def initialize(incr)
@incr = incr
end
end
end
\ No newline at end of file
...@@ -2379,7 +2379,6 @@ Now that we've referenced that class, it will be required for us. You'll notice ...@@ -2379,7 +2379,6 @@ Now that we've referenced that class, it will be required for us. You'll notice
* initialize_subscriber * initialize_subscriber
* set_clear_dependencies_hook * set_clear_dependencies_hook
* initialize_dependency_mechanism * initialize_dependency_mechanism
* bootstrap_load_path
These are all defined using the +initializer+ method: These are all defined using the +initializer+ method:
...@@ -2930,7 +2929,6 @@ With +@@autoloads+ being ...@@ -2930,7 +2929,6 @@ With +@@autoloads+ being
* initialize_subscriber * initialize_subscriber
* set_clear_dependencies_hook * set_clear_dependencies_hook
* initialize_dependency_mechanism * initialize_dependency_mechanism
* bootstrap_load_path
h4. Active Support Initializers h4. Active Support Initializers
......
...@@ -69,6 +69,8 @@ def inherited(base) ...@@ -69,6 +69,8 @@ def inherited(base)
raise "You cannot have more than one Rails::Application" if Rails.application raise "You cannot have more than one Rails::Application" if Rails.application
super super
Rails.application = base.instance Rails.application = base.instance
ActiveSupport.run_load_hooks(:before_configuration, base.instance)
end end
def respond_to?(*args) def respond_to?(*args)
...@@ -82,7 +84,7 @@ def method_missing(*args, &block) ...@@ -82,7 +84,7 @@ def method_missing(*args, &block)
end end
end end
delegate :metal_loader, :to => :config delegate :middleware, :metal_loader, :to => :config
def require_environment! def require_environment!
environment = paths.config.environment.to_a.first environment = paths.config.environment.to_a.first
...@@ -125,7 +127,7 @@ def load_generators ...@@ -125,7 +127,7 @@ def load_generators
end end
def app def app
@app ||= middleware.build(routes) @app ||= config.middleware.build(routes)
end end
def call(env) def call(env)
......
...@@ -10,7 +10,8 @@ module Bootstrap ...@@ -10,7 +10,8 @@ module Bootstrap
require environment if environment require environment if environment
end end
initializer :load_all_active_support do initializer :load_active_support do
require 'active_support/dependencies'
require "active_support/all" unless config.active_support.bare require "active_support/all" unless config.active_support.bare
end end
...@@ -18,7 +19,6 @@ module Bootstrap ...@@ -18,7 +19,6 @@ module Bootstrap
# Used by Passenger to ensure everything's loaded before forking and # Used by Passenger to ensure everything's loaded before forking and
# to avoid autoload race conditions in JRuby. # to avoid autoload race conditions in JRuby.
initializer :preload_frameworks do initializer :preload_frameworks do
require 'active_support/dependencies'
ActiveSupport::Autoload.eager_autoload! if config.preload_frameworks ActiveSupport::Autoload.eager_autoload! if config.preload_frameworks
end end
...@@ -66,8 +66,8 @@ module Bootstrap ...@@ -66,8 +66,8 @@ module Bootstrap
ActiveSupport::Dependencies.mechanism = config.cache_classes ? :require : :load ActiveSupport::Dependencies.mechanism = config.cache_classes ? :require : :load
end end
initializer :bootstrap_load_path do initializer :bootstrap_hook do |app|
# This is just an initializer used as hook so all load paths are loaded together ActiveSupport.run_load_hooks(:before_initialize, app)
end end
end end
end end
......
...@@ -33,7 +33,7 @@ def encoding=(value) ...@@ -33,7 +33,7 @@ def encoding=(value)
end end
def middleware def middleware
@middleware ||= default_middleware_stack @middleware ||= app_middleware.merge_into(default_middleware_stack)
end end
def metal_loader def metal_loader
......
...@@ -35,10 +35,8 @@ module Finisher ...@@ -35,10 +35,8 @@ module Finisher
app app
end end
initializer :after_initialize do initializer :finisher_hook do |app|
config.after_initialize_blocks.each do |block| ActiveSupport.run_load_hooks(:after_initialize, app)
block.call(self)
end
end end
# Disable dependency loading during request cycle # Disable dependency loading during request cycle
......
...@@ -125,7 +125,7 @@ def find_root_with_flag(flag, default=nil) ...@@ -125,7 +125,7 @@ def find_root_with_flag(flag, default=nil)
end end
end end
delegate :middleware, :paths, :root, :to => :config delegate :paths, :root, :to => :config
def load_tasks def load_tasks
super super
...@@ -133,7 +133,7 @@ def load_tasks ...@@ -133,7 +133,7 @@ def load_tasks
end end
# Add configured load paths to ruby load paths and remove duplicates. # Add configured load paths to ruby load paths and remove duplicates.
initializer :set_load_path, :before => :bootstrap_load_path do initializer :set_load_path, :before => :bootstrap_hook do
config.load_paths.reverse_each do |path| config.load_paths.reverse_each do |path|
$LOAD_PATH.unshift(path) if File.directory?(path) $LOAD_PATH.unshift(path) if File.directory?(path)
end end
...@@ -142,7 +142,10 @@ def load_tasks ...@@ -142,7 +142,10 @@ def load_tasks
# Set the paths from which Rails will automatically load source files, # Set the paths from which Rails will automatically load source files,
# and the load_once paths. # and the load_once paths.
initializer :set_autoload_paths, :before => :bootstrap_load_path do |app| #
# This needs to be an initializer, since it needs to run once
# per engine and get the engine as a block parameter
initializer :set_autoload_paths, :before => :bootstrap_hook do |app|
ActiveSupport::Dependencies.load_paths.unshift(*config.load_paths) ActiveSupport::Dependencies.load_paths.unshift(*config.load_paths)
if reloadable?(app) if reloadable?(app)
...@@ -200,7 +203,9 @@ def load_tasks ...@@ -200,7 +203,9 @@ def load_tasks
end end
end end
initializer :load_app_classes do |app| # This needs to be an initializer, since it needs to run once
# per engine and get the engine as a block parameter
initializer :load_app_classes, :before => :finisher_hook do |app|
next if $rails_rake_task next if $rails_rake_task
if app.config.cache_classes if app.config.cache_classes
......
...@@ -3,10 +3,50 @@ ...@@ -3,10 +3,50 @@
module Rails module Rails
class Railtie class Railtie
class Configuration class Configuration
class MiddlewareStackProxy
def initialize
@operations = []
end
def insert_before(*args, &block)
@operations << [:insert_before, args, block]
end
alias insert insert_before
def insert_after(*args, &block)
@operations << [:insert_after, args, block]
end
def swap(*args, &block)
@operations << [:swap, args, block]
end
def use(*args, &block)
@operations << [:use, args, block]
end
def merge_into(other)
@operations.each do |operation, args, block|
other.send(operation, *args, &block)
end
other
end
end
def initialize def initialize
@@options ||= {} @@options ||= {}
end end
# This allows you to modify the application's middlewares from Engines.
#
# All operations you run on the app_middleware will be replayed on the
# application once it is defined and the default_middlewares are
# created
def app_middleware
@@app_middleware ||= MiddlewareStackProxy.new
end
# Holds generators configuration: # Holds generators configuration:
# #
# config.generators do |g| # config.generators do |g|
...@@ -28,12 +68,8 @@ def generators ...@@ -28,12 +68,8 @@ def generators
end end
end end
def after_initialize_blocks def after_initialize(&block)
@@after_initialize_blocks ||= [] ActiveSupport.on_load(:after_initialize, :yield => true, &block)
end
def after_initialize(&blk)
after_initialize_blocks << blk if blk
end end
def to_prepare_blocks def to_prepare_blocks
......
...@@ -28,19 +28,6 @@ def setup ...@@ -28,19 +28,6 @@ def setup
assert_equal "congratulations", $test_after_initialize_block2 assert_equal "congratulations", $test_after_initialize_block2
end end
test "after_initialize block works correctly when no block is passed" do
add_to_config <<-RUBY
config.root = "#{app_path}"
config.after_initialize { $test_after_initialize_block1 = "success" }
config.after_initialize # don't pass a block, this is what we're testing!
config.after_initialize { $test_after_initialize_block2 = "congratulations" }
RUBY
require "#{app_path}/config/environment"
assert_equal "success", $test_after_initialize_block1
assert_equal "congratulations", $test_after_initialize_block2
end
test "after_initialize runs after frameworks have been initialized" do test "after_initialize runs after frameworks have been initialized" do
$activerecord_configurations = nil $activerecord_configurations = nil
add_to_config <<-RUBY add_to_config <<-RUBY
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册