require File.join(File.dirname(__FILE__), 'routing', 'urls') module Mack # This is the heart and soul of the Mack framework! This class interfaces with the Rack framework. # It handles all the dispatching back and forth between the Rack framework and a Mack application. class Runner include Extlib::Hook include Mack::Routes::Urls attr_reader :response # :nodoc: attr_reader :request # :nodoc: attr_reader :cookies # :nodoc: # This method needs to be defined as part of the Rack framework. As is noted for the Mack::Runner # class, this is where the center of the Mack framework lies. def call(env) # pp env begin setup(env) do begin route = Mack::Routes::RouteMap.instance.get_route_from_request(self.request) if route[:redirect_to] # because the route is specified to be a redirect, let's do that: redirect_to(route) else self.request.all_params[:original_controller] = route[:controller] self.request.all_params[:original_action] = route[:action] run_controller(route) end # rescue Mack::Errors::ResourceNotFound, Mack::Errors::UndefinedRoute => e # return try_to_find_resource(env, e) rescue Exception => e route = Mack::Routes::RouteMap.instance.get_route_from_error(e.class) unless route.nil? run_controller(route, e) else if e.class == Mack::Errors::ResourceNotFound || e.class == Mack::Errors::UndefinedRoute return try_to_find_resource(env, e) else raise e end end end end # setup rescue Exception => e Mack.logger.error(e) raise e end end #-- # This method gets called after the session has been established. Override this method # to add custom code around requests. # def custom_dispatch_wrapper # yield # end #++ #private def run_controller(route, e = nil) # let's handle a normal request: begin cont = "#{route[:controller].to_s.camelcase}Controller".constantize rescue NameError => e raise Mack::Errors::ResourceNotFound.new(self.request.path_info) end self.request.all_params[:controller] = route[:controller] self.request.all_params[:action] = route[:action] self.request.instance_variable_set("@params_controller", nil) self.request.instance_variable_set("@params_action", nil) c = cont.new c.configure_controller(self.request, self.response, self.cookies) c.caught_exception = e unless e.nil? self.response.controller = c self.response.write(c.run) end def log_request s_time = Time.now x = yield e_time = Time.now p_time = e_time - s_time if app_config.log.detailed_requests msg = "\n\t[#{@request.request_method.upcase}] '#{@request.path_info}'\n" msg << "\tSession ID: #{@request.session.id}\n" msg << "\tParameters: #{@request.all_params}\n" msg << "\tCompleted in #{p_time} (#{(1 / p_time).round} reqs/sec) | #{@response.status} [#{@request.full_host}]" else msg = "[#{@request.request_method.upcase}] '#{@request.path_info}' (#{p_time})" end Mack.logger.info(msg) x end # Setup the request, response, cookies, session, etc... # yield up, and then clean things up afterwards. def setup(env) exception = nil log_request do @request = Mack::Request.new(env) @response = Mack::Response.new @cookies = Mack::CookieJar.new(self.request, self.response) session do begin # custom_dispatch_wrapper do yield # end rescue Exception => e exception = e end end end raise exception if exception self.response.finish end def session sess_id = self.cookies[app_config.mack.session_id] unless sess_id sess_id = create_new_session else sess = Cachetastic::Caches::MackSessionCache.get(sess_id) if sess self.request.session = sess else # we couldn't find it in the store, so we need to create it: sess_id = create_new_session end end yield Cachetastic::Caches::MackSessionCache.set(sess_id, self.request.session) end def create_new_session id = String.randomize(40).downcase self.cookies[app_config.mack.session_id] = {:value => id, :expires => nil} sess = Mack::Session.new(id) self.request.session = sess Cachetastic::Caches::MackSessionCache.set(id, sess) id end def try_to_find_resource(env, exception) env = env.dup # we can't find a route for this, so let's try and see if it's in the public directory: if File.extname(env["PATH_INFO"]).blank? env["PATH_INFO"] << ".html" end if File.exists?(File.join(Mack.root, "public", env["PATH_INFO"])) return Rack::File.new(File.join(Mack.root, "public")).call(env) else raise exception end end # This will redirect the request to the specified url. A default status of # 302, Moved Temporarily, is set if no status is specified. A simple HTML # page is rendered in case the redirect does not occur. def redirect_to(route) status = route[:status] || 302 url = route[:redirect_to] options = self.request.all_params options.merge!(route) options - [:controller, :action, :redirect_to, :method, :status, :format] url = url_for_pattern(url, options) self.response.status = status self.response[:location] = url self.response.write(redirect_html(self.request.path_info, url, status)) end end end