require Merb.framework_root / "merb-core" / "dispatch" / "default_exception" / "default_exception" module Merb class Dispatcher class << self include Merb::ControllerExceptions attr_accessor :use_mutex @@work_queue = Queue.new # ==== Returns # Queue:: the current queue of dispatch jobs. # # @api private def work_queue @@work_queue end Merb::Dispatcher.use_mutex = ::Merb::Config[:use_mutex] # Dispatch the rack environment. ControllerExceptions are rescued here # and redispatched. # # ==== Parameters # rack_env:: # The rack environment, which is used to instantiate a Merb::Request # # ==== Returns # Merb::Controller:: # The Merb::Controller that was dispatched to # # @api private def handle(request) request.handle end end end class Request include Merb::ControllerExceptions @@mutex = Mutex.new # Handles request routing and action dispatch. # # ==== Returns # Merb::Controller:: the controller that handled the action dispatch. # # @api private def handle start = Time.now Merb.logger.info "Started request handling: #{start.to_s}" find_route! return rack_response if handled? klass = controller Merb.logger.debug("Routed to: #{params.inspect}") unless klass < Controller raise NotFound, "Controller '#{klass}' not found.\n" \ "If Merb tries to find a controller for static files, " \ "you may need to check your Rackup file, see the Problems " \ "section at: http://wiki.merbivore.com/pages/rack-middleware" end if klass.abstract? raise NotFound, "The '#{klass}' controller has no public actions" end controller = dispatch_action(klass, params[:action]) controller._benchmarks[:dispatch_time] = Time.now - start Merb.logger.info controller._benchmarks.inspect Merb.logger.flush controller.rack_response rescue Object => exception dispatch_exception(exception).rack_response end private # Setup the controller and call the chosen action # # ==== Parameters # klass:: The controller class to dispatch to. # action:: The action to dispatch. # status:: The status code to respond with. # # ==== Returns # Merb::Controller:: # The Merb::Controller that was dispatched to. # # @api private def dispatch_action(klass, action, status=200) # build controller controller = klass.new(self, status) if Dispatcher.use_mutex @@mutex.synchronize { controller._dispatch(action) } else controller._dispatch(action) end controller end # Re-route the current request to the Exception controller if it is # available, and try to render the exception nicely. # # You can handle exceptions by implementing actions for specific # exceptions such as not_found or for entire classes of exceptions # such as client_error. You can also implement handlers for # exceptions outside the Merb exception hierarchy (e.g. # StandardError is caught in standard_error). # # ==== Parameters # exception:: # The exception object that was created when trying to dispatch the # original controller. # # ==== Returns # Exceptions:: # The Merb::Controller that was dispatched to. # # @api private def dispatch_exception(exception) if(exception.is_a?(Merb::ControllerExceptions::Base) && !exception.is_a?(Merb::ControllerExceptions::ServerError)) Merb.logger.info(Merb.exception(exception)) else Merb.logger.error(Merb.exception(exception)) end self.exceptions = [exception] begin e = exceptions.first if action_name = e.action_name dispatch_action(Exceptions, action_name, e.class.status) else Merb::Dispatcher::DefaultException.new(self, e.class.status)._dispatch end rescue Object => dispatch_issue if e.same?(dispatch_issue) || exceptions.size > 5 Merb::Dispatcher::DefaultException.new(self, e.class.status)._dispatch else Merb.logger.error("Dispatching #{e.class} raised another error.") Merb.logger.error(Merb.exception(dispatch_issue)) exceptions.unshift dispatch_issue retry end end end end end