The Dispatcher manages a set of controllers. It selects the appropriate Controller and action to handle the given request.
This dispatcher intelligently handles RESTful uris according to the following scheme:
GET /links GET /links/index Link::Controller#index POST /links POST /links/create Link::Controller#create GET /links;new GET /links/new Link::Controller#new GET /links/1 Link::Controller#view(1) GET /links/1;edit GET /links/edit/1 Link::Controller#edit(1) PUT /links/1 POST /links/update/1 Link::Controller#update DELETE /links/1 GET /links/delete/1 Link::Controller#delete(1) GET /links/index.xml Link::Controller#index # Atom GET /links/index.json Link::Controller#index # JSON
The default actions for the various methods are:
GET: index POST: create PUT: update
DELETE: delete
| [RW] | controllers | The hash that maps mount paths to controllers. |
| [RW] | formats | The representation formats this dispatcher understands. |
| [RW] | router | The (optional) router. |
Initialize the dispatcher.
[ show source ]
# File lib/raw/dispatcher.rb, line 50
50: def initialize(controller_or_map = nil)
51: @controllers = {}
52: @formats = Nitro::STANDARD_FORMATS.dup
53:
54: if controller_or_map.is_a?(Class)
55: mount("/" => controller_or_map)
56: elsif controller_or_map.is_a?(Hash)
57: mount(controller_or_map)
58: end
59: end
Return the controller for the given mount path.
[ show source ]
# File lib/raw/dispatcher.rb, line 71
71: def [](path = "/")
72: @controllers[path]
73: end
Mount a controller to the given mount path.
[ show source ]
# File lib/raw/dispatcher.rb, line 77
77: def []=(path, controller)
78: controller = resolve_controller(controller)
79:
80: # Customize the class for mounting at the given path.
81: controller.mount_at(path) if controller.respond_to? :mount_at
82:
83: # Call the mounted callback to allow for post mount
84: # initialization.
85: controller.mounted(path) if controller.respond_to? :mounted
86:
87: @controllers[path] = controller
88: end
Dispatch a path given the request method. This method handles fully resolved paths (containing an extension that denotes the expected content type).
This method automatically handles ‘nice’ (seo friendly, elegant) parameters, ie:
/links/view/1
instead of
/links/view?oid=1
Output
controller, action, query_string, nice_params, extension
[ show source ]
# File lib/raw/dispatcher.rb, line 118
118: def dispatch(uri, method = :get)
119: # Extract the query string.
120:
121: path, query = uri.split("?", 2)
122:
123: # Try to route the path.
124:
125: path = @router.route(path) if @router
126:
127: # The characters after the last '.' in the path form the
128: # extension that itself represents the expected response
129: # content type.
130:
131: ext = File.extname(path)[1..-1] || "html"
132:
133: # The resource representation format for this request.
134:
135: unless format = Context.current.format = @formats.by_extension[ext]
136: raise ActionError.new("Cannot respond to '#{path}' using the '#{ext}' format representation.")
137: end
138:
139: # Remove the extension from the path.
140:
141: path = path.gsub(/\.(.*)$/, '')
142:
143: # Try to extract the controller from the path (that may also
144: # include 'nice' parameters). This algorithm tries to find
145: # the bigest substring of the path that represents a mount
146: # path for a controller.
147:
148: key = path.dup
149:
150: while (controller = @controllers[key]).nil?
151: key = key[%r{^(/.+)/.+$}, 1] || '/'
152: end
153:
154: # Try to extract the controller from the path. This
155: # algorithm tries to find the bigest substring of the path
156: # that represents an action of this controller.
157: #
158: # The algorithm respects action name conventions, ie
159: # simple/sub/action maps to simple__sub__action.
160:
161: action = key = path.sub(%r{^#{key}}, '').gsub(%r{^/}, '').gsub(%r{/}, '__')
162:
163: while (!action.blank?) and !controller.action_or_template?(action, format)
164: action = action[%r{^(.+)__.+$}, 1]
165: end
166:
167: # Extract the 'nice' parameters.
168:
169: params = key.sub(%r{^#{action}}, '').gsub(/^__/, '').split('__')
170:
171: # Do we have an action?
172:
173: if action.blank?
174: # Try to use a standard action for this http method.
175:
176: case method
177: when :get
178: action = "index"
179:
180: when :post
181: action = "create"
182:
183: when :delete
184: action = "delete"
185:
186: when :put
187: action = "update"
188: end
189:
190: unless controller.action_or_template?(action, format)
191: raise ActionError.new("Cannot respond to '#{path}' using '#{controller}'")
192: end
193: end
194:
195: return controller, "#{action}___super", query, params, ext
196: end
Alias for #dispatch_request
Dispatch a request. Calls the lower level dispatch method.
[ show source ]
# File lib/raw/dispatcher.rb, line 92
92: def dispatch_request(request)
93: dispatch(request.uri, request.method)
94: end
Mounts a map of controllers.
[ show source ]
# File lib/raw/dispatcher.rb, line 63
63: def mount(map)
64: for path, controller in map
65: self[path] = controller
66: end
67: end
[ show source ]
# File lib/raw/dispatcher/mounter.rb, line 52
52: def root
53: if controller = self["/"]
54: Mounter.new(self, controller)
55: end
56: end
An alternative mounting mechanism (CherryPy like).
Example
dispatcher.root = RootController dispatcher.root.users = User::Controller dispatcher.root.users.comments = User::Comment::Controller
[ show source ]
# File lib/raw/dispatcher/mounter.rb, line 48
48: def root=(controller)
49: self["/"] = resolve_controller(controller)
50: end