mirror of
https://github.com/github/rails.git
synced 2026-04-26 03:00:59 -04:00
Extract generic callbacks middleware from dispatcher
This commit is contained in:
@@ -1,46 +1,11 @@
|
||||
require 'active_support/core_ext/module/delegation'
|
||||
|
||||
module ActionController
|
||||
# Dispatches requests to the appropriate controller and takes care of
|
||||
# reloading the app after each request when Dependencies.load? is true.
|
||||
class Dispatcher
|
||||
class << self
|
||||
def define_dispatcher_callbacks(cache_classes)
|
||||
unless cache_classes
|
||||
# Development mode callbacks
|
||||
before_dispatch :reload_application
|
||||
after_dispatch :cleanup_application
|
||||
|
||||
ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
|
||||
end
|
||||
|
||||
if defined?(ActiveRecord)
|
||||
to_prepare(:activerecord_instantiate_observers) { ActiveRecord::Base.instantiate_observers }
|
||||
end
|
||||
|
||||
after_dispatch :flush_logger if Base.logger && Base.logger.respond_to?(:flush)
|
||||
|
||||
to_prepare do
|
||||
I18n.reload!
|
||||
end
|
||||
end
|
||||
|
||||
# Add a preparation callback. Preparation callbacks are run before every
|
||||
# request in development mode, and before the first request in production
|
||||
# mode.
|
||||
#
|
||||
# An optional identifier may be supplied for the callback. If provided,
|
||||
# to_prepare may be called again with the same identifier to replace the
|
||||
# existing callback. Passing an identifier is a suggested practice if the
|
||||
# code adding a preparation block may be reloaded.
|
||||
def to_prepare(identifier = nil, &block)
|
||||
@prepare_dispatch_callbacks ||= ActiveSupport::Callbacks::CallbackChain.new
|
||||
callback = ActiveSupport::Callbacks::Callback.new(:prepare_dispatch, block, :identifier => identifier)
|
||||
@prepare_dispatch_callbacks.replace_or_append!(callback)
|
||||
end
|
||||
|
||||
def run_prepare_callbacks
|
||||
new.send :run_callbacks, :prepare_dispatch
|
||||
end
|
||||
end
|
||||
cattr_accessor :prepare_each_request
|
||||
self.prepare_each_request = false
|
||||
|
||||
cattr_accessor :router
|
||||
self.router = Routing::Routes
|
||||
@@ -51,37 +16,50 @@ module ActionController
|
||||
middleware.instance_eval(File.read(middlewares), middlewares, 1)
|
||||
end
|
||||
|
||||
include ActiveSupport::Callbacks
|
||||
define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch
|
||||
class << self
|
||||
def define_dispatcher_callbacks(cache_classes)
|
||||
unless cache_classes
|
||||
# Run prepare callbacks before every request in development mode
|
||||
self.prepare_each_request = true
|
||||
|
||||
def initialize
|
||||
@app = @@middleware.build(@@router)
|
||||
freeze
|
||||
end
|
||||
# Development mode callbacks
|
||||
ActionDispatch::Callbacks.before_dispatch do |app|
|
||||
ActionController::Dispatcher.router.reload
|
||||
end
|
||||
|
||||
def call(env)
|
||||
run_callbacks :before_dispatch
|
||||
@app.call(env)
|
||||
ensure
|
||||
run_callbacks :after_dispatch, :enumerator => :reverse_each
|
||||
end
|
||||
ActionDispatch::Callbacks.after_dispatch do
|
||||
# Cleanup the application before processing the current request.
|
||||
ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
|
||||
ActiveSupport::Dependencies.clear
|
||||
ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
|
||||
end
|
||||
|
||||
def reload_application
|
||||
# Run prepare callbacks before every request in development mode
|
||||
run_callbacks :prepare_dispatch
|
||||
ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
|
||||
end
|
||||
|
||||
@@router.reload
|
||||
end
|
||||
if defined?(ActiveRecord)
|
||||
to_prepare(:activerecord_instantiate_observers) do
|
||||
ActiveRecord::Base.instantiate_observers
|
||||
end
|
||||
end
|
||||
|
||||
def cleanup_application
|
||||
# Cleanup the application before processing the current request.
|
||||
ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
|
||||
ActiveSupport::Dependencies.clear
|
||||
ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
|
||||
end
|
||||
if Base.logger && Base.logger.respond_to?(:flush)
|
||||
after_dispatch do
|
||||
Base.logger.flush
|
||||
end
|
||||
end
|
||||
|
||||
def flush_logger
|
||||
Base.logger.flush
|
||||
to_prepare do
|
||||
I18n.reload!
|
||||
end
|
||||
end
|
||||
|
||||
delegate :to_prepare, :prepare_dispatch, :before_dispatch, :after_dispatch,
|
||||
:to => ActionDispatch::Callbacks
|
||||
|
||||
def new
|
||||
@@middleware.build(@@router)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,6 +3,7 @@ use "Rack::Lock", :if => lambda {
|
||||
}
|
||||
|
||||
use "ActionDispatch::ShowExceptions", lambda { ActionController::Base.consider_all_requests_local }
|
||||
use "ActionDispatch::Callbacks", lambda { ActionController::Dispatcher.prepare_each_request }
|
||||
use "ActionDispatch::Rescue", lambda {
|
||||
controller = (::ApplicationController rescue ActionController::Base)
|
||||
# TODO: Replace with controller.action(:_rescue_action)
|
||||
|
||||
@@ -38,6 +38,7 @@ module ActionDispatch
|
||||
autoload :Response, 'action_dispatch/http/response'
|
||||
autoload :StatusCodes, 'action_dispatch/http/status_codes'
|
||||
|
||||
autoload :Callbacks, 'action_dispatch/middleware/callbacks'
|
||||
autoload :ParamsParser, 'action_dispatch/middleware/params_parser'
|
||||
autoload :Rescue, 'action_dispatch/middleware/rescue'
|
||||
autoload :ShowExceptions, 'action_dispatch/middleware/show_exceptions'
|
||||
|
||||
40
actionpack/lib/action_dispatch/middleware/callbacks.rb
Normal file
40
actionpack/lib/action_dispatch/middleware/callbacks.rb
Normal file
@@ -0,0 +1,40 @@
|
||||
module ActionDispatch
|
||||
class Callbacks
|
||||
include ActiveSupport::Callbacks
|
||||
define_callbacks :prepare, :before, :after
|
||||
|
||||
class << self
|
||||
# DEPRECATED
|
||||
alias_method :prepare_dispatch, :prepare
|
||||
alias_method :before_dispatch, :before
|
||||
alias_method :after_dispatch, :after
|
||||
end
|
||||
|
||||
# Add a preparation callback. Preparation callbacks are run before every
|
||||
# request in development mode, and before the first request in production
|
||||
# mode.
|
||||
#
|
||||
# An optional identifier may be supplied for the callback. If provided,
|
||||
# to_prepare may be called again with the same identifier to replace the
|
||||
# existing callback. Passing an identifier is a suggested practice if the
|
||||
# code adding a preparation block may be reloaded.
|
||||
def self.to_prepare(identifier = nil, &block)
|
||||
@prepare_callbacks ||= ActiveSupport::Callbacks::CallbackChain.new
|
||||
callback = ActiveSupport::Callbacks::Callback.new(:prepare, block, :identifier => identifier)
|
||||
@prepare_callbacks.replace_or_append!(callback)
|
||||
end
|
||||
|
||||
def initialize(app, prepare_each_request = false)
|
||||
@app, @prepare_each_request = app, prepare_each_request
|
||||
run_callbacks :prepare
|
||||
end
|
||||
|
||||
def call(env)
|
||||
run_callbacks :before
|
||||
run_callbacks :prepare if @prepare_each_request
|
||||
@app.call(env)
|
||||
ensure
|
||||
run_callbacks :after, :enumerator => :reverse_each
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -6,20 +6,20 @@ class DispatcherTest < Test::Unit::TestCase
|
||||
def setup
|
||||
ENV['REQUEST_METHOD'] = 'GET'
|
||||
|
||||
Dispatcher.middleware = ActionDispatch::MiddlewareStack.new do |middleware|
|
||||
middlewares = File.expand_path(File.join(File.dirname(__FILE__), "../../lib/action_controller/dispatch/middlewares.rb"))
|
||||
middleware.instance_eval(File.read(middlewares))
|
||||
end
|
||||
|
||||
# Clear callbacks as they are redefined by Dispatcher#define_dispatcher_callbacks
|
||||
Dispatcher.instance_variable_set("@prepare_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
||||
Dispatcher.instance_variable_set("@before_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
||||
Dispatcher.instance_variable_set("@after_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
||||
ActionDispatch::Callbacks.instance_variable_set("@prepare_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
||||
ActionDispatch::Callbacks.instance_variable_set("@before_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
||||
ActionDispatch::Callbacks.instance_variable_set("@after_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
||||
|
||||
@old_router, Dispatcher.router = Dispatcher.router, mock()
|
||||
Dispatcher.router.stubs(:call).returns([200, {}, 'response'])
|
||||
Dispatcher.router.stubs(:reload)
|
||||
Dispatcher.stubs(:require_dependency)
|
||||
end
|
||||
|
||||
def teardown
|
||||
Dispatcher.router = @old_router
|
||||
@dispatcher = nil
|
||||
ENV.delete 'REQUEST_METHOD'
|
||||
end
|
||||
|
||||
@@ -29,12 +29,12 @@ class DispatcherTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_reloads_routes_before_dispatch_if_in_loading_mode
|
||||
ActionController::Routing::Routes.expects(:reload).once
|
||||
Dispatcher.router.expects(:reload).once
|
||||
dispatch(false)
|
||||
end
|
||||
|
||||
def test_leaves_dependencies_after_dispatch_if_not_in_loading_mode
|
||||
ActionController::Routing::Routes.expects(:reload).never
|
||||
Dispatcher.router.expects(:reload).never
|
||||
ActiveSupport::Dependencies.expects(:clear).never
|
||||
|
||||
dispatch
|
||||
@@ -55,7 +55,7 @@ class DispatcherTest < Test::Unit::TestCase
|
||||
assert_nil a || b || c
|
||||
|
||||
# Run callbacks
|
||||
Dispatcher.run_prepare_callbacks
|
||||
dispatch
|
||||
|
||||
assert_equal 1, a
|
||||
assert_equal 2, b
|
||||
@@ -72,16 +72,22 @@ class DispatcherTest < Test::Unit::TestCase
|
||||
Dispatcher.to_prepare(:unique_id) { |*args| a = b = 1 }
|
||||
Dispatcher.to_prepare(:unique_id) { |*args| a = 2 }
|
||||
|
||||
Dispatcher.run_prepare_callbacks
|
||||
dispatch
|
||||
assert_equal 2, a
|
||||
assert_equal nil, b
|
||||
end
|
||||
|
||||
private
|
||||
def dispatch(cache_classes = true)
|
||||
ActionController::Routing::RouteSet.any_instance.stubs(:call).returns([200, {}, 'response'])
|
||||
ActionController::Dispatcher.prepare_each_request = false
|
||||
Dispatcher.define_dispatcher_callbacks(cache_classes)
|
||||
Dispatcher.new.call({'rack.input' => StringIO.new('')})
|
||||
Dispatcher.middleware = ActionDispatch::MiddlewareStack.new do |middleware|
|
||||
middlewares = File.expand_path(File.join(File.dirname(__FILE__), "../../lib/action_controller/dispatch/middlewares.rb"))
|
||||
middleware.instance_eval(File.read(middlewares))
|
||||
end
|
||||
|
||||
@dispatcher ||= Dispatcher.new
|
||||
@dispatcher.call({'rack.input' => StringIO.new(''), 'action_dispatch.show_exceptions' => false})
|
||||
end
|
||||
|
||||
def assert_subclasses(howmany, klass, message = klass.subclasses.inspect)
|
||||
|
||||
@@ -26,8 +26,7 @@ end
|
||||
#reloads the environment
|
||||
def reload!
|
||||
puts "Reloading..."
|
||||
dispatcher = ActionController::Dispatcher.new
|
||||
dispatcher.cleanup_application
|
||||
dispatcher.reload_application
|
||||
ActionController::Dispatcher.new
|
||||
ActionController::Dispatcher.router.reload
|
||||
true
|
||||
end
|
||||
|
||||
@@ -632,7 +632,6 @@ Run `rake gems:install` to install the missing gems.
|
||||
return unless configuration.frameworks.include?(:action_controller)
|
||||
require 'dispatcher' unless defined?(::Dispatcher)
|
||||
Dispatcher.define_dispatcher_callbacks(configuration.cache_classes)
|
||||
Dispatcher.run_prepare_callbacks
|
||||
end
|
||||
|
||||
def disable_dependency_loading
|
||||
|
||||
Reference in New Issue
Block a user