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>