public
Description: A Ruby web application framework
Homepage: http://www.mackframework.com
Clone URL: git://github.com/markbates/mack.git
Click here to lend your support to: mack and make a donation at www.pledgie.com !
mack / lib / runner.rb
100644 174 lines (160 sloc) 6.026 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
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