Polished up the formatting and code for the output and separated out the colorizing...
[maraby.git] / mrouter / mrouter.rb
blob33352bd4d8cc02888ead3eefffcb174e5f0f5509
1 #--
2 #  Created by Matt Todd on 2008-01-02.
3 #  Copyright (c) 2007. All rights reserved.
4 #++
6 #--
7 # dependencies
8 #++
10 begin
11   %w(rubygems merb/core_ext merb/router).each {|dep|require dep}
12 rescue LoadError => e
13   abort "Merb must be installed for Routing to function. Please install Merb."
14 end
16 #--
17 # module
18 #++
20 module Rack
21   
22   # = MRouter, the Rack Merb Router
23   # 
24   # == Usage
25   # 
26   #   class RackApp
27   #     include MRouterHelper
28   #     route do |r|
29   #       r.match('/path/to/match').to(:action => 'do_stuff')
30   #       {:action => 'not_found'} # the default route
31   #     end
32   #     def do_stuff(params)
33   #       [200, {}, 'OK']
34   #     end
35   #     def call(env)
36   #       [200, {'Content-Type'=>'text/plain'}, env['merb.route'].inspect]
37   #     end
38   #   end
39   # 
40   # == Default Routes
41   # 
42   # Supplying a default route if none of the others match is good practice,
43   # but is unnecessary as the predefined route is always, automatically,
44   # going to contain a redirection to the +not_found+ method. (So your app
45   # should implement a +not_found+ method.)
46   # 
47   # In order to set a different default route, simply end the call to +route+
48   # with a hash containing the action to run along with any other params.
49   # 
50   # If your particular app requires another field to default to, such as a
51   # controller of some sort, defining a default route will be necessary as
52   # MRouter only knows it should default to +not_found+.
53   # 
54   # == The Hard Work
55   # 
56   # The mechanics of the router are solely from the efforts of the Merb
57   # community. This functionality is completely ripped right out of Merb
58   # and makes it functional. All credit to them, and be sure to check out
59   # their great framework: if you need a beefier framework, maybe Merb is
60   # right for you.
61   # 
62   # http://merbivore.com/
63   class MRouter < Merb::Router
64     
65     def initialize(app)
66       @app = app
67     end
68     
69     def call(env)
70       env['merb.router'] = self
71       env['merb.route'] = self.class.route(env)
72       @app.call(env)
73     end
74     
75     # Retrieves the last value from the +route+ call and, if it's a Hash, sets
76     # it to +@@default_route+ to designate the failover route. If +route+ is
77     # not a Hash, though, the internal default should be used instead (as the
78     # last returned value is probably a Route object returned by the
79     # <tt>r.match().to()</tt> call).
80     # 
81     # Used exclusively internally.
82     def self.default_to route
83       @@default_route = route.is_a?(Hash) ? route : {:action => 'not_found'}
84     end
85     
86     # Called internally by the +call+ method to match the current request
87     # against the currently defined routes. Returns the params list defined in
88     # the +to+ routing definition, opting for the default route if no match is
89     # made.
90     def self.route(env)
91       # pull out the path requested (WEBrick keeps the host and port and protocol in REQUEST_URI)
92       uri = URI.parse(env['REQUEST_URI']).path
93       
94       # prepare request
95       path = (uri ? uri.split('?').first : '').sub(/\/+/, '/')
96       path = path[0..-2] if (path[-1] == ?/) && path.size > 1
97       req = Struct.new(:path, :method).new(path, env['REQUEST_METHOD'].downcase.to_sym)
98       
99       # perform match
100       route = self.match(req, {})
101       
102       # make sure a route is returned even if no match is found
103       if route[0].nil?
104         #return default route
105         @@default_route
106       else
107         # params (including action and module if set) for the matching route
108         route[1]
109       end
110     end
111     
112   end
115 # The Helper that provides the +route+ class method and +route+ instance method
116 # to setup routes and to actually route the request.
117 module MRouterHelper
118   
119   # Matches the request information in +env+ to the appropriate route.
120   def route
121     Rack::MRouter.route(@env)
122   end
123   
124   module ClassMethods
125     # Yields the router which allows routes to be set up.
126     def route
127       if block_given?
128         Rack::MRouter.prepare do |router|
129           Rack::MRouter.default_to yield(router)
130         end
131       else
132         abort "Halcyon::Server::Base.route expects a block to define routes."
133       end
134     end
135   end
136   
137   # Hook to include the class methods into the receiver.
138   def self.included(receiver)
139     receiver.extend(ClassMethods)
140   end
141