Upgraded Rails and RSpec
[monkeycharger.git] / vendor / rails / actionpack / lib / action_controller / dispatcher.rb
blobdf63e0c99244da49d91e34b4b24dcf6aa60f1300
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.
4   class Dispatcher
5     class << self
6       # Backward-compatible class method takes CGI-specific args. Deprecated
7       # in favor of Dispatcher.new(output, request, response).dispatch!
8       def dispatch(cgi = nil, session_options = CgiRequest::DEFAULT_SESSION_OPTIONS, output = $stdout)
9         new(output).dispatch_cgi(cgi, session_options)
10       end
12       # Declare a block to be called before each dispatch.
13       # Run in the order declared.
14       def before_dispatch(*method_names, &block)
15         callbacks[:before].concat method_names
16         callbacks[:before] << block if block_given?
17       end
19       # Declare a block to be called after each dispatch.
20       # Run in reverse of the order declared.
21       def after_dispatch(*method_names, &block)
22         callbacks[:after].concat method_names
23         callbacks[:after] << block if block_given?
24       end
26       # Add a preparation callback. Preparation callbacks are run before every
27       # request in development mode, and before the first request in production
28       # mode.
29       # 
30       # An optional identifier may be supplied for the callback. If provided,
31       # to_prepare may be called again with the same identifier to replace the
32       # existing callback. Passing an identifier is a suggested practice if the
33       # code adding a preparation block may be reloaded.
34       def to_prepare(identifier = nil, &block)
35         # Already registered: update the existing callback
36         if identifier
37           if callback = callbacks[:prepare].assoc(identifier)
38             callback[1] = block
39           else
40             callbacks[:prepare] << [identifier, block]
41           end
42         else
43           callbacks[:prepare] << block
44         end
45       end
47       # If the block raises, send status code as a last-ditch response.
48       def failsafe_response(fallback_output, status, originating_exception = nil)
49         yield
50       rescue Exception => exception
51         begin
52           log_failsafe_exception(status, originating_exception || exception)
53           body = failsafe_response_body(status)
54           fallback_output.write "Status: #{status}\r\nContent-Type: text/html\r\n\r\n#{body}"
55           nil
56         rescue Exception => failsafe_error # Logger or IO errors
57           $stderr.puts "Error during failsafe response: #{failsafe_error}"
58           $stderr.puts "(originally #{originating_exception})" if originating_exception
59         end
60       end
62       private
63         def failsafe_response_body(status)
64           error_path = "#{error_file_path}/#{status.to_s[0..3]}.html"
66           if File.exist?(error_path)
67             File.read(error_path)
68           else
69             "<html><body><h1>#{status}</h1></body></html>"
70           end
71         end
73         def log_failsafe_exception(status, exception)
74           message = "/!\\ FAILSAFE /!\\  #{Time.now}\n  Status: #{status}\n"
75           message << "  #{exception}\n    #{exception.backtrace.join("\n    ")}" if exception
76           failsafe_logger.fatal message
77         end
79         def failsafe_logger
80           if defined?(::RAILS_DEFAULT_LOGGER) && !::RAILS_DEFAULT_LOGGER.nil?
81             ::RAILS_DEFAULT_LOGGER
82           else
83             Logger.new($stderr)
84           end
85         end
86     end
88     cattr_accessor :error_file_path
89     self.error_file_path = "#{::RAILS_ROOT}/public" if defined? ::RAILS_ROOT
91     cattr_accessor :callbacks
92     self.callbacks = Hash.new { |h, k| h[k] = [] }
94     cattr_accessor :unprepared
95     self.unprepared = true
98     before_dispatch :reload_application
99     before_dispatch :prepare_application
100     after_dispatch :flush_logger
101     after_dispatch :cleanup_application
103     if defined? ActiveRecord
104       to_prepare :activerecord_instantiate_observers do
105         ActiveRecord::Base.instantiate_observers
106       end
107     end
109     def initialize(output, request = nil, response = nil)
110       @output, @request, @response = output, request, response
111     end
113     def dispatch
114       run_callbacks :before
115       handle_request
116     rescue Exception => exception
117       failsafe_rescue exception
118     ensure
119       run_callbacks :after, :reverse_each
120     end
122     def dispatch_cgi(cgi, session_options)
123       if cgi ||= self.class.failsafe_response(@output, '400 Bad Request') { CGI.new }
124         @request = CgiRequest.new(cgi, session_options)
125         @response = CgiResponse.new(cgi)
126         dispatch
127       end
128     rescue Exception => exception
129       failsafe_rescue exception
130     end
132     def reload_application
133       if Dependencies.load?
134         Routing::Routes.reload
135         self.unprepared = true
136       end
137     end
139     def prepare_application(force = false)
140       begin
141         require_dependency 'application' unless defined?(::ApplicationController)
142       rescue LoadError => error
143         raise unless error.message =~ /application\.rb/
144       end
146       ActiveRecord::Base.verify_active_connections! if defined?(ActiveRecord)
148       if unprepared || force
149         run_callbacks :prepare
150         self.unprepared = false
151       end
152     end
154     # Cleanup the application by clearing out loaded classes so they can
155     # be reloaded on the next request without restarting the server.
156     def cleanup_application(force = false)
157       if Dependencies.load? || force
158         ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
159         Dependencies.clear
160         ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
161       end
162     end
164     def flush_logger
165       RAILS_DEFAULT_LOGGER.flush if defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER.respond_to?(:flush)
166     end
168     protected
169       def handle_request
170         @controller = Routing::Routes.recognize(@request)
171         @controller.process(@request, @response).out(@output)
172       end
174       def run_callbacks(kind, enumerator = :each)
175         callbacks[kind].send!(enumerator) do |callback|
176           case callback
177           when Proc; callback.call(self)
178           when String, Symbol; send!(callback)
179           when Array; callback[1].call(self)
180           else raise ArgumentError, "Unrecognized callback #{callback.inspect}"
181           end
182         end
183       end
185       def failsafe_rescue(exception)
186         self.class.failsafe_response(@output, '500 Internal Server Error', exception) do
187           if @controller ||= defined?(::ApplicationController) ? ::ApplicationController : Base
188             @controller.process_with_exception(@request, @response, exception).out(@output)
189           else
190             raise exception
191           end
192         end
193       end
194   end