1 module ActionController
2 # Dispatches requests to the appropriate controller and takes care of
3 # reloading the app after each request when Dependencies.load? is true.
8 def define_dispatcher_callbacks(cache_classes
)
10 # Development mode callbacks
11 before_dispatch
:reload_application
12 after_dispatch
:cleanup_application
16 to_prepare
:load_application_controller do
18 require_dependency
'application' unless defined?(::ApplicationController)
19 rescue LoadError
=> error
20 raise unless error
.message
=~
/application\.rb/
24 if defined?(ActiveRecord
)
25 after_dispatch
:checkin_connections
26 to_prepare(:activerecord_instantiate_observers) { ActiveRecord
::Base.instantiate_observers
}
29 after_dispatch
:flush_logger if Base
.logger
&& Base
.logger
.respond_to
?(:flush)
36 # Backward-compatible class method takes CGI-specific args. Deprecated
37 # in favor of Dispatcher.new(output, request, response).dispatch.
38 def dispatch(cgi
= nil, session_options
= CgiRequest
::DEFAULT_SESSION_OPTIONS, output
= $stdout)
39 new(output
).dispatch_cgi(cgi
, session_options
)
42 # Add a preparation callback. Preparation callbacks are run before every
43 # request in development mode, and before the first request in production
46 # An optional identifier may be supplied for the callback. If provided,
47 # to_prepare may be called again with the same identifier to replace the
48 # existing callback. Passing an identifier is a suggested practice if the
49 # code adding a preparation block may be reloaded.
50 def to_prepare(identifier
= nil, &block
)
51 @prepare_dispatch_callbacks ||= ActiveSupport
::Callbacks::CallbackChain.new
52 callback
= ActiveSupport
::Callbacks::Callback.new(:prepare_dispatch, block
, :identifier => identifier
)
53 @prepare_dispatch_callbacks.replace_or_append
!(callback
)
56 # If the block raises, send status code as a last-ditch response.
57 def failsafe_response(fallback_output
, status
, originating_exception
= nil)
59 rescue Exception
=> exception
61 log_failsafe_exception(status
, originating_exception
|| exception
)
62 body
= failsafe_response_body(status
)
63 fallback_output
.write
"Status: #{status}\r\nContent-Type: text/html\r\n\r\n#{body}"
65 rescue Exception
=> failsafe_error
# Logger or IO errors
66 $stderr.puts
"Error during failsafe response: #{failsafe_error}"
67 $stderr.puts
"(originally #{originating_exception})" if originating_exception
72 def failsafe_response_body(status
)
73 error_path
= "#{error_file_path}/#{status.to_s[0..3]}.html"
75 if File
.exist
?(error_path
)
78 "<html><body><h1>#{status}</h1></body></html>"
82 def log_failsafe_exception(status
, exception
)
83 message
= "/!\\ FAILSAFE /!\\ #{Time.now}\n Status: #{status}\n"
84 message
<< " #{exception}\n #{exception.backtrace.join("\n ")}" if exception
85 failsafe_logger
.fatal message
89 if defined?(::RAILS_DEFAULT_LOGGER) && !::RAILS_DEFAULT_LOGGER.nil?
90 ::RAILS_DEFAULT_LOGGER
97 cattr_accessor
:error_file_path
98 self.error_file_path
= Rails
.public_path
if defined?(Rails
.public_path
)
100 include ActiveSupport
::Callbacks
101 define_callbacks
:prepare_dispatch, :before_dispatch, :after_dispatch
103 def initialize(output
= $stdout, request
= nil, response
= nil)
104 @output, @request, @response = output
, request
, response
107 def dispatch_unlocked
109 run_callbacks
:before_dispatch
111 rescue Exception
=> exception
112 failsafe_rescue exception
114 run_callbacks
:after_dispatch, :enumerator => :reverse_each
119 if ActionController
::Base.allow_concurrency
122 @
@guard.synchronize
do
128 def dispatch_cgi(cgi
, session_options
)
129 if cgi
||= self.class.failsafe_response(@output, '400 Bad Request') { CGI
.new
}
130 @request = CgiRequest
.new(cgi
, session_options
)
131 @response = CgiResponse
.new(cgi
)
134 rescue Exception
=> exception
135 failsafe_rescue exception
139 @request = RackRequest
.new(env)
140 @response = RackResponse
.new(@request)
144 def reload_application
145 # Run prepare callbacks before every request in development mode
146 run_callbacks
:prepare_dispatch
148 Routing
::Routes.reload
149 ActionController
::Base.view_paths
.reload
!
150 ActionView
::Helpers::AssetTagHelper::AssetTag::Cache.clear
153 # Cleanup the application by clearing out loaded classes so they can
154 # be reloaded on the next request without restarting the server.
155 def cleanup_application
156 ActiveRecord
::Base.reset_subclasses
if defined?(ActiveRecord
)
157 ActiveSupport
::Dependencies.clear
158 ActiveRecord
::Base.clear_reloadable_connections
! if defined?(ActiveRecord
)
165 def mark_as_test_request
!
174 def checkin_connections
175 # Don't return connection (and peform implicit rollback) if this request is a part of integration test
176 return if test_request
?
177 ActiveRecord
::Base.clear_active_connections
!
182 @controller = Routing
::Routes.recognize(@request)
183 @controller.process(@request, @response).out(@output)
186 def failsafe_rescue(exception
)
187 self.class.failsafe_response(@output, '500 Internal Server Error', exception
) do
188 if @controller ||= defined?(::ApplicationController) ? ::ApplicationController : Base
189 @controller.process_with_exception(@request, @response, exception
).out(@output)