Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

root/tags/rel_1-2-1/railties/lib/dispatcher.rb

Revision 5658, 6.6 kB (checked in by david, 2 years ago)

If only life was that simple (it didnt help)

Line 
1 #--
2 # Copyright (c) 2004-2006 David Heinemeier Hansson
3 #
4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be
13 # included in all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 #++
23
24 # This class provides an interface for dispatching a CGI (or CGI-like) request
25 # to the appropriate controller and action. It also takes care of resetting
26 # the environment (when Dependencies.load? is true) after each request.
27 class Dispatcher
28  
29   class << self
30
31     # Dispatch the given CGI request, using the given session options, and
32     # emitting the output via the given output.  If you dispatch with your
33     # own CGI object be sure to handle the exceptions it raises on multipart
34     # requests (EOFError and ArgumentError).
35     def dispatch(cgi = nil, session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, output = $stdout)
36       controller = nil
37       if cgi ||= new_cgi(output)
38         request, response = ActionController::CgiRequest.new(cgi, session_options), ActionController::CgiResponse.new(cgi)
39         prepare_application
40         controller = ActionController::Routing::Routes.recognize(request)
41         controller.process(request, response).out(output)
42       end
43     rescue Exception => exception  # errors from CGI dispatch
44       failsafe_response(output, '500 Internal Server Error', exception) do
45         controller ||= ApplicationController rescue LoadError nil
46         controller ||= ActionController::Base
47         controller.process_with_exception(request, response, exception).out(output)
48       end
49     ensure
50       # Do not give a failsafe response here.
51       reset_after_dispatch
52     end
53
54     # Reset the application by clearing out loaded controllers, views, actions,
55     # mailers, and so forth. This allows them to be loaded again without having
56     # to restart the server (WEBrick, FastCGI, etc.).
57     def reset_application!
58       ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
59
60       Dependencies.clear
61       ActiveSupport::Deprecation.silence do # TODO: Remove after 1.2
62         Class.remove_class(*Reloadable.reloadable_classes)
63       end
64        
65       ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
66     end
67    
68     # Add a preparation callback. Preparation callbacks are run before every
69     # request in development mode, and before the first request in production
70     # mode.
71     #
72     # An optional identifier may be supplied for the callback. If provided,
73     # to_prepare may be called again with the same identifier to replace the
74     # existing callback. Passing an identifier is a suggested practice if the
75     # code adding a preparation block may be reloaded.
76     def to_prepare(identifier = nil, &block)
77       unless identifier.nil?
78         callback = preparation_callbacks.detect { |ident, _| ident == identifier }
79
80         if callback # Already registered: update the existing callback
81           callback[-1] = block
82           return
83         end
84       end
85
86       preparation_callbacks << [identifier, block]
87
88       return
89     end
90
91     private
92
93       attr_accessor :preparation_callbacks, :preparation_callbacks_run
94       alias_method :preparation_callbacks_run?, :preparation_callbacks_run
95      
96       # CGI.new plus exception handling.  CGI#read_multipart raises EOFError
97       # if body.empty? or body.size != Content-Length and raises ArgumentError
98       # if Content-Length is non-integer.
99       def new_cgi(output)
100         failsafe_response(output, '400 Bad Request') { CGI.new }
101       end
102
103       def prepare_application
104         if Dependencies.load?
105           ActionController::Routing::Routes.reload
106           self.preparation_callbacks_run = false
107         end
108
109         prepare_breakpoint
110         require_dependency 'application' unless Object.const_defined?(:ApplicationController)
111         ActiveRecord::Base.verify_active_connections! if defined?(ActiveRecord)
112         run_preparation_callbacks
113       end
114
115       def reset_after_dispatch
116         reset_application! if Dependencies.load?
117         Breakpoint.deactivate_drb if defined?(BREAKPOINT_SERVER_PORT)
118       end
119
120       def prepare_breakpoint
121         return unless defined?(BREAKPOINT_SERVER_PORT)
122         require 'breakpoint'
123         Breakpoint.activate_drb("druby://localhost:#{BREAKPOINT_SERVER_PORT}", nil, !defined?(FastCGI))
124         true
125       rescue
126         nil
127       end
128
129       def run_preparation_callbacks
130         return if preparation_callbacks_run?
131         preparation_callbacks.each { |_, callback| callback.call }
132         self.preparation_callbacks_run = true
133       end
134
135       # If the block raises, send status code as a last-ditch response.
136       def failsafe_response(output, status, exception = nil)
137         yield
138       rescue Exception  # errors from executed block
139         begin
140           output.write "Status: #{status}\r\n"
141          
142           if exception
143             message    = exception.to_s + "\r\n" + exception.backtrace.join("\r\n")
144             error_path = File.join(RAILS_ROOT, 'public', '500.html')
145
146             if defined?(RAILS_DEFAULT_LOGGER) && !RAILS_DEFAULT_LOGGER.nil?
147               RAILS_DEFAULT_LOGGER.fatal(message)
148
149               output.write "Content-Type: text/html\r\n\r\n"
150
151               if File.exists?(error_path)
152                 output.write(IO.read(error_path))
153               else
154                 output.write("<html><body><h1>Application error (Rails)</h1></body></html>")
155               end
156             else
157               output.write "Content-Type: text/plain\r\n\r\n"
158               output.write(message)
159             end
160           end
161         rescue Exception  # Logger or IO errors
162         end
163       end
164   end
165  
166   self.preparation_callbacks = []
167   self.preparation_callbacks_run = false
168  
169 end
170
171 Dispatcher.to_prepare :activerecord_instantiate_observers do
172   ActiveRecord::Base.instantiate_observers
173 end if defined?(ActiveRecord)
Note: See TracBrowser for help on using the browser.