lib/nitro/dispatcher.rb in nitro-0.31.0 vs lib/nitro/dispatcher.rb in nitro-0.40.0
- old
+ new
@@ -6,20 +6,24 @@
module Nitro
# Raised when an action can not be found for a path
# check for this in your error action to catch as if 404
-class NoActionError < NoMethodError; end
+class ActionError < NoMethodError; end
# The Dispatcher manages a set of controllers. It maps
# a request uri to a [controller, action] pair.
class Dispatcher
include Router
ROOT = '/'
+
+ # The server.
+ attr_accessor :server
+
# The controllers map.
attr_accessor :controllers
# Create a new Dispatcher.
@@ -67,23 +71,27 @@
unless (klass.ancestors.include?(Controller) or klass.ancestors.include?(Publishable))
klass.send :include, Publishable
end
# Automatically mixin controller helpers.
-
+
mixin_auto_helpers(klass)
# Customize the class for mounting at the given path.
#--
# gmosx, TODO: path should include trailing '/'
# gmosx, TODO: should actually create an instance, thus
# allowing mounting the same controller to multiple
# paths, plus simplifying the code. This instance will
# be dup-ed for each request.
#++
-
+
klass.mount_at(path)
+
+ # Call the mounted callback to allow for post mount
+ # initialization.
+
klass.mounted(path) if klass.respond_to?(:mounted)
end
(@controllers ||= {}).update(controllers)
@@ -98,11 +106,13 @@
# default helper 'Helper' and the auto helper
# 'XxxControllerHelper' (if it exists) are included.
def mixin_auto_helpers(klass)
klass.helper(Nitro::DefaultHelper)
-
+
+ return # FIXME: make the following work again!
+
begin
if helper = Module.by_name("#{klass}Helper")
klass.helper(helper)
end
rescue NameError
@@ -126,19 +136,18 @@
@controllers.each do |base, c|
base = '' if base == '/'
for m in c.action_methods
m = m.to_sym
if route = c.ann(m).route and (!route.nil?)
- add_rule(route.first, :controller => c, :action => m, :params => route.last)
+ add_rule(:match => route.first, :controller => c, :action => m, :params => route.last)
end
end
end
end
# Processes the path and dispatches to the corresponding
# controller/action pair.
- # The base returned contains a trailing '/'.
#
# [+path+]
# The path to dispatch.
#
# [:context]
@@ -147,13 +156,13 @@
# The dispatching algorithm handles implicit nice urls.
# Subdirectories are also supported.
# Action containing '/' separators look for templates
# in subdirectories. The '/' char is converted to '__'
# to find the actual action.
+ # The dispatcher also handles nested controllers.
#
- # Returns the dispatcher class, the action name and the
- # base url. For the root path, the base url is nil.
+ # Returns the dispatcher class and the action name.
#--
# FIXME: this is a critical method that should be optimized
# watch out for excessive String creation.
# TODO: add caching.
#++
@@ -161,66 +170,98 @@
def dispatch(path, context = nil)
# Try if the router can directly decode the path.
klass, action, params = decode_route(path)
if klass
- context.params.update(params) if params
+ # This adds parameter values from the setup from the route to the normal
+ # query strings.
+ context.headers['QUERY_STRING'] ||= ''
+ extra = params.map { |k, v| "#{k}=#{v}"}.join(';') if params
+ if context.headers['QUERY_STRING'].empty?
+ context.headers['QUERY_STRING'] = extra
+ else
+ context.headers['QUERY_STRING'] << ';' << extra
+ end
+
+ context.headers['ACTION_PARAMS'] = params.values
+
+ # context.params.update(params) if params
# gmosx, FIXME/OPTIMIZE: no annotation for mount point!!
return klass, "#{action}_action", klass.mount_path
end
+
+ key, * = path.split('?', 2)
+ key = key.split('/')
+ parts = []
- path = path.sub(/#{Router.strip_path}/, '') if Router.strip_path
- parts = path.split('/')
- parts.shift # get rid of the leading '/'.
+ while (not key.empty?) and (klass = controller_class_for("#{key.join('/')}")).nil?
+ parts.unshift(key.pop)
+ end
+
+ klass = controller_class_for(ROOT) unless klass
- if klass = controller_class_for("/#{parts.first}")
- base = "/#{parts.shift}"
- else
- base = nil
- klass = controller_class_for(ROOT)
- end
-
idx = 0
found = false
+
+ # gmosx, HACKFIX!
- # default to index
-
- parts << 'index' if parts.empty?
-
+ parts.shift if parts.first == ''
+
# Try to find the first valid action substring
action = ''
-
+
for part in parts
action << part
if klass.respond_to_action_or_template?(action)
found = true
break
end
action << '__'
idx += 1
end
+
+ # Check the index action.
+
+ unless found
+ action = :index
+ if klass.respond_to_action? action
+ a = klass.instance_method(action).arity
+ found = true if a < 0 || a >= parts.size
+ elsif klass.respond_to_template? action
+ found = true if parts.size == 0
+ end
+ idx = -1 if found
+ end
if found
parts.slice!(0, idx + 1)
+=begin
+ if $DBG
+ # Some extra checking of the parameters. Only enabled
+ # on debug mode, because it slows down dispatching.
+
+ a = klass.instance_method(action).arity
+
+ if a > 0 and a != parts.size
+ raise ActionError, "Invalid parameters for action, expects #{a} parameters, received #{parts.size}"
+ end
+ end
+=end
else
#--
# FIXME: no raise to make testable.
#++
- raise NoActionError, "No action for path '#{path}' on '#{klass}'"
+ raise ActionError, "No action for path '#{path}' on '#{klass}'"
end
# push any remaining parts of the url onto the query
# string for use with request
- unless parts.empty?
- context.headers['QUERY_STRING'] = "#{parts.join(';')};#{context.headers['QUERY_STRING']}"
- end
+ context.headers['ACTION_PARAMS'] = parts
- base = nil if base == ROOT
-
- return klass, "#{action}_action", base
+ return klass, "#{action}_action"
end
alias_method :split_path, :dispatch
private
@@ -242,8 +283,5 @@
end
end
end
-
-# * George Moschovitis <gm@navel.gr>
-# * Chris Farmiloe <chris.farmiloe@farmiloe.com>