Extract generic callbacks middleware from dispatcher

This commit is contained in:
Joshua Peek
2009-05-17 13:39:44 -05:00
parent edc9c226d1
commit 092089015b
7 changed files with 106 additions and 82 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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'

View 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

View File

@@ -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)

View File

@@ -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

View File

@@ -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