module Nitro require 'nitro/controller' require 'nitro/routing' require 'nitro/simple' # The Dispatcher manages a set of controllers. class Dispatcher include Router # The public root directory. The files in this # directory are published by the web server. attr_accessor :public_root # The template root directory. By default this # points to the public root directory to allow # for PHP/JSP/ASP style programming. But you should # probably change this to another directory for # extra security. attr_accessor :template_root # The controllers map. attr_accessor :controllers # Create a new Dispatcher. # # Input: # # [+controllers+] # Either a hash of controller mappings or a single # controller that gets mapped to :root. def initialize(controllers = nil) @public_root = 'public' @template_root = @public_root if controllers and controllers.is_a?(Class) and controllers.ancestors.include?(Controller) controllers = { :root => controllers } else controllers ||= { :root => SimpleController } end mount(controllers) end # Process the given hash and mount the # defined controllers. # # Input: # # [+controllers+] # A hash representing the mapping of # mount points to controllers. # # === Examples # # dispatcher.mount( # :root => MainController, # mounts / # 'users' => UsersController # mounts /users # ) def add_controller(controllers) (@controllers ||= {}).update(controllers) update_routes end alias_method :mount, :add_controller # Update the routes. Typically called after a new # Controller is mounted. def update_routes @routes = [] @controllers.each do |base, c| base = (base == :root ? '' : "/#{base}") c.action_metadata.each do |action, meta| if route = meta[:route] @routes << [route, "#{base}/#{action}", *meta.params.keys] 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] # The dispatching context. def dispatch(path, context = nil) path = route(path, context) parts = path.split('/') parts.shift case parts.size when 0 # / -> root.index base = '/' klass = controller_class_for(:root, context) action = 'index' when 1 if klass = controller_class_for(parts[0], context) # controller/ -> controller.index base = "/#{parts[0]}" action = 'index' else # action/ -> root.action base = '/' klass = controller_class_for(:root, context) action = parts[0] end else if klass = controller_class_for(parts[0], context) # controller/action -> controller.action base = "/#{parts[0]}" parts.shift action = parts.join('__') else # action/ -> root.action base = '/' klass = controller_class_for(:root, context) action = parts.join('__') end end return klass, "#{action}_action", base end alias_method :split_path, :dispatch # Get the controller for the given key. # Also handles reloading of controllers. #-- # gmosx, FIXME: this method is a NASTY hack, anyone can # help me fix this ? #++ def controller_class_for(key, context) klass = @controllers[key] if (:full == Rendering.reload) and context and context[:__RELOADED__].nil? and klass #=begin ancestors = [c = klass] while (c = c.superclass) != Nitro::Controller ancestors.unshift(c) end for c in ancestors actions = c.public_instance_methods.find_all { |m| m.to_s =~ /(_action$)|(_template$)/ } actions += c.action_methods actions.each { |m| c.send(:remove_method, m) rescue nil } c.advices = [] Object.send(:remove_const, c) rescue nil load(c::DEF_FILE) c = Object.const_get(klass.name.intern) end #=end =begin def_file = klass::DEF_FILE # Object.send(:remove_const, klass) rescue nil # FIXME: also reload superclasses! Controller.remove_subclasses load(def_file) =end klass = @controllers[key] = Object.const_get(klass.name.intern) context[:__RELOADED__] = true end return klass end end end # * George Moschovitis