Changeset 9111

Show
Ignore:
Timestamp:
06/13/08 11:46:44 (3 months ago)
Author:
matthias
Message:

* Got rid of the loader module, switched to importModule on request
* API CHANGE: Changed the way routes should be defined in the name_routes.js files -> .draw now works more like in rails -> for an example take a look into app/routes/say_routes.js
* changed the way how controllers should be declared. Aida now expects an NameController function, which will be used as the constructor for the controller in the name_controller.js file

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • sandbox/aida/demo-app/app/controllers/article_controller.js

    r9100 r9111  
    77importModule('app.orm.hibernate', 'db'); 
    88 
    9 this.actions = { 
     9function ArticleController(req, res, session) {    
     10 
     11   this.actions = { 
    1012    
    11    index : function() { 
    12       this.context.articles = db.find('from Article a order by a.createTime desc') 
    13    }, 
     13      index : function() { 
     14         this.context.articles = db.find('from Article a order by a.createTime desc') 
     15      }, 
    1416    
    15    "new" : function() {}, 
     17      "new" : function() {}, 
    1618 
    17    create : function() { 
    18       model.doCreate(req.data); 
    19       res.redirect('/blog'); 
    20    }, 
     19      create : function() { 
     20         model.doCreate(req.data); 
     21         res.redirect('/blog'); 
     22      }, 
    2123 
    22    show : function() { 
    23       this.context.article = db.get('Article', req.data.id); 
    24    }, 
     24      show : function() { 
     25         this.context.article = db.get('Article', req.data.id); 
     26      }, 
    2527 
    26    edit : function() { 
    27       this.context.article = db.get('Article', req.data.id);       
    28    }, 
     28      edit : function() { 
     29         this.context.article = db.get('Article', req.data.id);       
     30      }, 
    2931 
    30    update : function() { 
    31       model.doUpdate(req.data); 
    32       res.redirect('/blog/' + req.data.id);    
    33    }, 
     32      update : function() { 
     33         logger.info(uneval(req.data)) 
     34         model.doUpdate(req.data); 
     35         res.redirect('/blog/'); 
     36         return; 
     37         res.write("danke"); return; 
     38         res.redirect('/blog/' + req.data.id);    
     39      }, 
    3440    
    35    "delete" : function() { 
    36       this.context.article = db.get('Article', req.data.id); 
    37    }, 
     41      "delete" : function() { 
     42         this.context.article = db.get('Article', req.data.id); 
     43      }, 
    3844    
    39    destroy : function() { 
    40       model.doDelete(req.data.id); 
    41       res.redirect('/blog'); 
    42    }    
     45      destroy : function() { 
     46         model.doDelete(req.data.id); 
     47         res.redirect('/blog'); 
     48      } 
     49   } 
    4350    
    4451} 
  • sandbox/aida/demo-app/app/controllers/root_controller.js

    r9100 r9111  
    11importFromModule("aida.controller", "*"); 
    22 
    3 function index_action() { 
    4    this.context.hello = "Hello World!"; 
    5    // calls views/root/index.html 
     3function RootController(req, res, session) { 
     4       
     5   this.index_action = function() { 
     6      this.context.hello = "Hello World!"; 
     7      // calls views/root/index.html 
     8   } 
     9 
     10   this.ejs_action = function() { 
     11      this.context.hello = "Hello World!";    
     12   }   
     13 
    614} 
    7  
    8 function ejs_action() { 
    9    this.context.hello = "Hello World!";    
    10 } 
    11  
    12 function env_action() { 
    13    window.location = "http://alistapart.com/"; 
    14    res.write("global.window:" + window.document) 
    15    return; 
    16    var o = new Object(); 
    17    load("foo.js"); 
    18    return; 
    19     
    20    window.onload = function(){ 
    21      res.write("Newest A List Apart Posts:"); 
    22      $("h4.title").each(function(){ 
    23        res.write(" - " + this.textContent); 
    24      }); 
    25    }; 
    26 } 
  • sandbox/aida/demo-app/app/controllers/say_controller.js

    r9100 r9111  
    22 
    33foo("bar", "Bar"); 
     4var foo1 = "Foo 1"; 
    45 
    5 function SayController() { 
    6    shell.writeln("---" + this.constructor.name) 
     6function foo2_action() { 
     7   // -> /say/foo2 
     8   // can't access res!! 
     9   // throws an error 
     10   res.write(foo1) 
    711} 
    812 
    9 function index_action() { 
    10    res.writeln("---" + this.getShortName()) 
    11    res.writeln("---" + this.routes.getAll()) 
    12    // this will call views/root/index.html by default 
     13function SayController(req, res, session) { 
     14    
     15   this.bla = "bla" 
     16    
     17   function hidden() { 
     18      res.writeln("oops"); 
     19   } 
     20    
     21   this.skin_action = function() { 
     22      // render("hello"); 
     23   } 
     24    
     25   this.index_action = function() { 
     26      res.writeln("---" + getShortName()) 
     27      res.writeln("---" + this.foo1) 
     28      res.writeln("---" + this.foo2) 
     29   } 
     30    
     31   this.actions = { 
     32      goodBye : function() { 
     33         render({ 
     34            inline : '<div> \ 
     35               <h2>Good Bye <% name %>!</h2> \ 
     36               <p>and have a nice day.</p> \ 
     37            </div>', 
     38            locals : { 
     39               name : "Matthias" 
     40            } 
     41         }); 
     42      }, 
     43      index2 : function() { 
     44         render(req, res, session); 
     45      } 
     46   } 
     47 
     48   this.actions.html = { 
     49      hello3 : function() { 
     50 
     51      }, 
     52      hello2 : function() { 
     53         res.writeln("xx") 
     54      } 
     55   } 
     56 
     57   this.hello_action = function() { 
     58      hidden(); 
     59      res.writeln("Hello World!" + __name__ + ":" + bar + ":" + getShortName()); 
     60   }    
     61    
    1362} 
    1463 
    15 actions = { 
    16    goodBye : function() { 
    17       render({ 
    18          inline : '<div> \ 
    19             <h2>Good Bye <% name %>!</h2> \ 
    20             <p>and have a nice day.</p> \ 
    21          </div>', 
    22          locals : { 
    23             name : "Matthias" 
    24          } 
    25       }); 
    26    }, 
    27    index2 : function() { 
    28       render(); 
    29    } 
    30 } 
    31  
    32 actions.html = { 
    33    hello3 : function() { 
    34        
    35    } 
    36 } 
    37  
    38 function hello_action() { 
    39    render({action:"hello2"}); 
    40    return; 
    41    hidden(); 
    42    res.writeln("Hello World!" + __name__ + ":" + bar + ":" + shortName()); 
    43 } 
    44  
    45 function hidden() { 
    46    res.writeln("oops"); 
    47 } 
    48  
  • sandbox/aida/demo-app/app/controllers/twitter_controller.js

    r9100 r9111  
    77importModule("aida.prototype", "prototype"); 
    88 
     9function TwitterController(req, res, session) { 
     10    
     11   this.index_action = function() {}; 
    912 
    10 function index_action() {} 
    11  
    12  
    13 function friends_action() { 
    14    if (!req.data.id) { 
    15       res.redirect("/twitter"); 
    16    }    
    17    this.timeline = twitter.userTimeline({ 
    18       id : req.data.id 
    19    }); 
     13   this.friends_action  = function () { 
     14      if (!req.data.id) { 
     15         res.redirect("/twitter"); 
     16      }    
     17      this.timeline = twitter.userTimeline({ 
     18         id : req.data.id 
     19      }); 
     20   } 
     21    
    2022} 
  • sandbox/aida/demo-app/app/routes/article_routes.js

    r9100 r9111  
    1 importModule("aida.routing", "routing"); 
     1importModule("aida.routing"); 
    22 
    3 ArticleController.routes.draw( routing.RESTFUL_ROUTES ); 
     3var routeSet = new aida.routing.RouteSet("article").add( 
     4   aida.routing.RESTFUL_ROUTES 
     5); 
  • sandbox/aida/demo-app/app/routes/root_routes.js

    r9090 r9111  
     1importFromModule("aida.routing", "RouteSet"); 
    12 
    2 RootController.routes.draw([  
     3var routeSet = new RouteSet("root").add([ 
    34   { 
    45      pattern : "/blog->", 
    5       forwardTo : ArticleController 
     6      forwardTo : "article" 
    67   },     
    7    { 
    8       pattern : "/$controller/$action/$id", 
    9    }       
     8   "/$controller/$action/$id"       
    109]); 
  • sandbox/aida/demo-app/app/views/article/delete.html.skin

    r9100 r9111  
    66</div> 
    77 
    8 <form action="/blog/<% param.id %>?method=DELETE" method="post" accept-charset="utf-8"> 
     8<form action="/blog/<% params.id %>?method=DELETE" method="post" accept-charset="utf-8"> 
    99  <input type="hidden" name="id" value="<% id %>" /> 
    1010  <p> 
  • sandbox/aida/demo-app/app/views/article/edit.html.skin

    r9100 r9111  
    1010</div> 
    1111 
    12 <form action="/blog/<% param.id %>?method=PUT" method="post" accept-charset="utf-8"> 
     12<form action="/blog/<% params.id %>?method=PUT" method="post" accept-charset="utf-8"> 
    1313  <input type="hidden" name="id" value="<% id %>" /> 
    1414  <p> 
  • sandbox/aida/demo-app/app/views/article/show.html.skin

    r9100 r9111  
    1414    <small> 
    1515      <i>posted: BUG!</i><br /> 
    16       <a href="/blog/<% param.id %>/edit">&raquo; Edit this article</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="/blog/<% param.id %>/delete">&raquo; Delete this article</a> 
     16      <a href="/blog/<% params.id %>/edit">&raquo; Edit this article</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="/blog/<% params.id %>/delete">&raquo; Delete this article</a> 
    1717    </small> 
    1818  </p> 
  • sandbox/aida/demo-app/main.js

    r9090 r9111  
    11importModule('helma.app', 'app'); 
    22importModule('helma.rhino', 'rhino'); 
     3importModule('helma.logging', 'logging'); 
     4importModule("helma.shell", "shell"); 
     5// this is just for convinience while developing/debugging aida - i will remove this in the future 
     6global.shell = shell; 
     7global.logger = logging.getLogger(__name__); 
     8 
    39importFromModule("helma.file", "File"); 
    4 global.APP_DIR = new File(""); 
     10// try to fix this with helma-ng 0.3 
     11global.APP_DIR = new File(getResource(".").name); 
    512 
    6 // load app dirs 
    7 importModule("modules.appLoader"); 
     13importModule("aida.controller"); 
     14importFromModule("aida.routing", "RouteSet"); 
    815 
    916// main method called to start application 
     
    1118    app.start({ staticDir: 'static' }); 
    1219} 
     20 
     21/** 
     22 * Helma-NG handler function that connects to the Helma servlet.  
     23 * 
     24 * @param {object} req     will be passed by helma-ng 
     25 * @param {object} res     will be passed by helma-ng 
     26 * @param {object} session will be passed by helma-ng 
     27 */ 
     28global.handleRequest = function(req, res, session) {    
     29   global.req = req; 
     30   global.res = res; 
     31   global.session = session; 
     32   aida.controller.handleRequest("root", req, res, session); 
     33} 
     34 
     35/* 
     36var rs = RootRoutes = new RouteSet("root").draw(function() { 
     37   this.connect("/blog->", { forwardTo : "article" }); 
     38   this.connect("/$controller/$action/$id");    
     39}); 
     40rs.add(["/my/own/route"]); 
     41res.writeln(rs.routes.join("\n")); 
     42res.writeln(rs.recognizePath("POST /say/hello")); 
     43// res.writeln(rs.generate({path:"/blog/hello"})); 
     44res.writeln(rs.generate({controller:"foo", action:"hello", id:"11"})); 
     45*/ 
  • sandbox/aida/modules/aida/controller.js

    r9100 r9111  
    44importModule('prototype'); 
    55importModule('routing'); 
     6 
    67importFromModule("loader", "loadHelpers"); 
    78importFromModule("config.environments.development", "config"); 
    89 
    9 function instance() { 
    10    this.context = {}; 
    11    return new this["ctor"](); 
    12 
    13  
     10this.context = {}; 
     11 
     12function getControllerInstance(name, req, res, session) { 
     13   importModule("app.controllers." + name + "_controller"); 
     14   var ctor = app.controllers[name + "_controller"][getClassNameFromName(name)]; 
     15   ctor.prototype = app.controllers[name + "_controller"]; 
     16   ctor.prototype.__name__ = getClassNameFromName(name); 
     17   var c = new ctor(req, res, session); 
     18   c.importHelpers("application") 
     19   c.importHelpers(name) 
     20   return c; 
     21
     22 
     23function getClassNameFromName(name) { 
     24   return name.capitalize() + "Controller"; 
     25
    1426 
    1527/** 
     
    1830 * 
    1931 * @param {object} controller   Controller object 
    20  * @param {object} req          Request object 
     32 * @param {object} req          Request object, passed by helma-ng 
     33 * @param {object} res          Response object, passed by helma-ng 
     34 * @param {object} session      Session object, passed by helma-ng 
    2135 */ 
    22 function handleRequest(controller, req) { 
    23    var route = req.route = req.route || routing.recognizeRequest(req, controller); 
     36function handleRequest(controllerName, req, res, session) { 
     37   var routeSet = routing.loadRoutes(controllerName).routeSet; 
     38   var route = req.route = req.route || routeSet.recognizeRequest(req);    
    2439   if (!route) return; // FIXME 404 
    25    logger.info("found route:" + route.toString()); 
     40   var controller = getControllerInstance(route.controllerName, req, res, session); 
     41    
    2642   Object.extend(req.data, route.params); 
     43   Object.extend(req.params, route.params); 
    2744    
    28    if (route.controller) controller = route.controller; 
    29    if (typeof controller === "string") controller = global[controller.capitalize() + "Controller"]; 
    3045   route.handler = controller.getAction(route); 
    31    logger.info("route.handler:"+route.handler) 
    3246   var content = ""; 
    3347   res.push(); 
    34    try { 
     48//   try { 
    3549      route.handler.call(controller);       
    36    } catch(e) { 
     50/*   } catch(e) { 
    3751      res.write(e.rhinoException); 
    38    } 
     52   } */ 
    3953   content = res.pop(); 
    40    if (!res.calledRender === true) controller.render(content);       
     54   (res.calledRender) ? res.write(content) : controller.render(); 
    4155} 
    4256 
     
    217231 */ 
    218232function render(options) { 
     233   if (!res.contentType) res.contentType = routing.Formats.getMimeType(req.route.format); 
    219234   if (!options) options = {}; 
    220235   if (res.calledRender) throw new DoubleRenderError(); 
     
    267282      } 
    268283   } else { 
    269       var type = determineTemplateType({action:action}); 
     284      var type = this.determineTemplateType({action:action, format:req.route.format}); 
    270285      if (type === "est") { 
    271286         var resource = this.getTemplateSource({action:action, type:type}); 
     
    276291         res.write(result); 
    277292      } else if (type === "skin") { 
    278          helma.skin.render(getTemplatePath({action:action, type:type}), context); 
     293         helma.skin.render(this.getTemplatePath({action:action, type:type}), context); 
    279294      } else if (type) { 
    280          helma.skin.render(getTemplatePath({action:action, type:type}), context); 
     295         helma.skin.render(this.getTemplatePath({action:action, type:type}), context); 
    281296      } else { 
    282          res.writeln("Couldn't find " + this.getTemplateSource(action)); 
     297         res.writeln("Couldn't find " + this.getTemplateSource({action:action, type:type || "skin"})); 
    283298      } 
    284299   } 
     
    294309 
    295310function determineTemplateType(options) { 
    296    var types = ["est", "ejs", "skin", ((req.route.format) || options.format)]; 
     311   var types = ["est", "ejs", "skin", options.format]; 
    297312   for (var i=0; i<types.length; i++) { 
    298       if (getTemplateSource(Object.extend(options, {type:types[i]})).exists()) { 
     313      if (this.getTemplateSource(Object.extend(options, {type:types[i]})).exists()) { 
    299314         return types[i]; 
    300315      } 
     
    304319 
    305320function getTemplateSource(options) { 
    306    return getResource(this.getTemplatePath(options)); 
     321   return this.getResource(this.getTemplatePath(options)); 
    307322} 
    308323 
    309324function getTemplatePath(options) { 
     325   logger.info(uneval(options)) 
    310326   var options = Object.extend({ 
    311       action : req.route.action
    312       controller : (typeof req.route.controller === "string") ? req.route.controller : req.route.controller.getShortName(), 
    313       format : (req.route.format) || "html", 
     327      action : "index"
     328      controller : this.getShortName(), 
     329      format : "html", 
    314330      type : "skin" 
    315    }, options) 
    316    return (config.templateRoot || "app/views") + '/' + options.controller + '/' + options.action + '.' + options.format + ((options.type != options.format) ? ('.' + options.type) : ''); 
     331   }, options || {}); 
     332   var result = (config.templateRoot || "app/views") + '/' + options.controller + '/' + options.action + '.' + options.format + ((options.type != options.format) ? ('.' + options.type) : ''); 
     333   return result; 
    317334} 
    318335 
     
    322339 
    323340function getShortName() { 
    324    var namePattern = /^(.*)Controller$/i  
     341   var namePattern = /^(.*)Controller$/; 
    325342   return this.__name__.match(namePattern)[1].toLowerCase(); 
    326343} 
  • sandbox/aida/modules/aida/routing.js

    r9100 r9111  
    11importModule("helma.shell", "shell"); 
    22importModule("prototype"); 
     3 
     4__shared__ = true; 
    35 
    46/** 
     
    1315 * generate your application in the file app/routes/root_routes.js: 
    1416 * <pre language=JavaScript> 
    15  *  RootController.routes.draw([ 
     17 *  var RootController.routes.draw([ 
    1618 *    { 
    1719 *       pattern : "/$controller/$action/$id" 
     
    128130 
    129131 
     132function loadRoutes(name) { 
     133   importModule("app.routes." + name + "_routes"); 
     134   return app.routes[name + "_routes"]; 
     135} 
     136 
     137 
    130138/** 
    131  * Tries to resolve the incoming request, for a given controller. 
    132  * If no controller is specified it will fall back to RootController. 
     139 * Tries to resolve the incoming request, for a given RouteSet. 
    133140 * 
    134  * @param {object} req            Request object 
    135  * @param {object} [controller]   Controller object, defaults to RootController 
    136  * @return {object} Returns the result object from Route.match() 
     141 * @param {Object} [RouteSet]     RoutSet, to be checked for matching routes. 
     142 * @param {Object} req            Request object 
     143 * @return {Object} Returns the result object from Route.match() 
    137144 */ 
    138 function recognizeRequest(req, controller) { 
     145function recognizeRequest(routeSet, req) { 
     146   var routes = routeSet.routes;    
    139147   var result; 
    140    var controller = controller || RootController.instance(); 
    141    var routes = (controller.routes || controller.__proto__.routes).getAll(); 
    142148   for (var i=0; i<routes.length; i++) { 
    143149      var result = routes[i].match(req); 
    144150      if (result) { 
    145151         if (result.forwardTo) { 
    146             return recognizeRequest(req, result.forwardTo) 
     152            var routeSet = loadRoutes(result.forwardTo).routeSet;             
     153            return recognizeRequest(routeSet, req) 
    147154         } else { 
    148             logger.info("found:" + result) 
    149155            return result;             
    150156         } 
     
    156162 
    157163/** 
    158  * Tries to resolve a path with optionals parameters, for a given controller. 
    159  * If no controller is specified it will fall back to RootController. 
     164 * Tries to resolve a path with optionals parameters, for a given RouteSet. 
    160165 * 
    161  * @param {string} path           Request path 
    162  * @param {object} options        Request ooptions 
    163  * @config {string} method        HTTP method 
    164  * @param {object} [controller]   Controller object, defaults to RootController 
    165  * @return {object} Returns the result object from Route.match() 
     166 * @param {Object} [RouteSet]     RoutSet, to be checked for matching routes. 
     167 * @param {String} path           Request path 
     168 * @param {Object} params         Request params 
     169 * @param {String} [params.method]  HTTP method (uppercase), defaults to "GET" 
     170 * @return {Object} Returns the result object from Route.match() 
    166171 */ 
    167 function recognizePath(path, options, controller) { 
    168    options.path = path; 
    169    return recognizeRequest(options, controller) 
     172function recognizePath(routeSet, path, params) { 
     173   var routes = routeSet.routes;    
     174   // detect HTTP method 
     175   var methodMatch = path.match(/([A-Z]+) /); 
     176   var method; 
     177   if (methodMatch) { 
     178      method = methodMatch[1]; 
     179      path = path.substr(methodMatch[0].length); 
     180   } else { 
     181      method = "GET"; 
     182   } 
     183   var req = Object.extend({ 
     184      method : method, 
     185      path : path 
     186   }, params || {}); 
     187   return recognizeRequest(routeSet, req) 
    170188} 
    171189 
     
    173191/** 
    174192 * Generates a URL for a route. 
     193 * 
     194 * @param {Object} [RouteSet]     RoutSet, to be checked for matching routes. 
     195 * @param {Object} options           Request params 
     196 * @param {String} [options.method]  HTTP method (uppercase), defaults to "GET" 
     197 * @param {Request} req              Request object 
     198 * @return {String} Returns the resulting path (URL) 
     199 * @return {null} Returns null if no path could be generated. 
    175200 */ 
    176 function generate(options, req, controller) { 
    177 //   return (options+req+controller) 
     201function generate(routeSet, options, req) { 
    178202   var result; 
    179    var controller = controller || RootController;    
    180    var req = req || global.req;    
    181    var routes = controller.routes.getAll(); 
     203   var routes = routeSet.routes; 
    182204   for (var i=0; i<routes.length; i++) { 
    183       var result = routes[i].getPath(controller, options, req); 
     205      var result = routes[i].getPath(options, req); 
    184206      if (result) return result; 
    185207   } 
     
    189211 
    190212/** 
    191  * Route constructor for new routes. 
    192  * You don't need to create your own Route objects. Just use .connect and .draw to add new routes. 
     213 * Set constructor for holding a set of routes for a controller. 
     214 * You don't need to create your the Route objects on your own.  
     215 * Just use .connect, .add and .draw to add new routes. 
    193216 *  
    194  * @param {object} controller  Controller object 
     217 * @param {Object} controllerName  Short name ("article" for "ArticleController") of the controller. 
    195218 */ 
    196 var Routes = function(controller) { 
    197    this.__proto__ = controller; 
    198  
     219var RouteSet = function(controllerName) { 
     220   if (controllerName == null) controllerName = "root";    
     221       
    199222   var routes = []; 
    200  
    201    /** 
    202     * Add a new route, or an array of new routes, specified in options. 
    203     * @param {object} options  Options 
    204     * @param {array}  options  Array of options 
     223    
     224   /** 
     225    * Add route(s) by passing a route defintion object, or an array of route  
     226    * definition objects. 
     227    * @param {Object} routeDefinitions    routeDefinitions 
     228    * @param {Array}  options             Array of options 
     229    * @see Route 
    205230    * @return Array of all routes 
    206231    */ 
    207    this.add = function(options) { 
    208       (options instanceof Array) ? this.draw(options) : this.connect(options); 
    209       return routes; 
    210    } 
    211     
    212    /** 
    213     * Add an array of routes specified in options. 
    214     * @param {array} options   Array of options 
    215     * @return {array} Array of all routes 
     232   this.add = function(routeDefinitions) { 
     233      if (!routeDefinitions instanceof Array) routeDefinitions = [routeDefinitions]; 
     234      for (var i=0; i<routeDefinitions.length; i++) { 
     235         var rd = routeDefinitions[i]; 
     236         (typeof rd === "string") ? 
     237            this.connect(rd) : 
     238            this.connect(rd.pattern, rd); 
     239      } 
     240      return this; 
     241   } 
     242    
     243   /** 
     244    * Rails like syntax for adding new routes, by providing a mapper.  
     245    * @param {Function} mapper   Function, within you can access this RouteSet via "this". 
     246    * @return {RouteSet} Array of all routes 
    216247    */    
    217    this.draw = function(options) { 
    218       // add new routes 
    219       for (var i=0; i<options.length; i++) { 
    220          this.connect(options[i]); 
    221       }       
    222       return routes; 
     248   this.draw = function(mapper) { 
     249      mapper.apply(this); 
     250      return this; 
    223251   } 
    224252   
    225253   /** 
    226     * Add a new route specified in options. 
    227     * @param {object} options   Options 
    228     * @return {object} Route 
     254    * Add a new route specified in pattern and options. 
     255    * @param {Object} options   Options for the new route. 
     256    * @see Route 
     257    * @return {Route} Returns the new Route, that was just added to this RouteSet. 
    229258    */    
    230    this.connect = function(options) { 
     259   this.connect = function(pattern, options) { 
    231260      return routes.push(  
    232          new Route( 
     261         new Route( controllerName, 
    233262            Object.extend({ 
    234                controller : controller 
    235             }, options
     263               pattern : pattern 
     264            }, options || {}
    236265         )  
    237266      ); 
     267      return this; 
    238268   } 
    239269 
     
    241271    * Call routing.recognizeRequest for this controller. 
    242272    * 
    243     * @param {object} req            Request object 
    244     * @return {object} Returns the result object from Route.match() 
     273    * @param {Object} req            Request object 
     274    * @return {Object} Returns the result object from Route.match() 
    245275    */    
    246276   this.recognizeRequest = function(req) { 
    247       return recognizeRequest(req, controller); 
     277      return recognizeRequest(this, req); 
    248278   }, 
    249279       
     
    251281    * Call routing.recognizePath for this controller. 
    252282    * 
    253     * @param {string} path           Request path 
    254     * @param {object} options        Request ooptions 
    255     * @config {string} method        HTTP method 
    256     * @return {object} Returns the result object from Route.match() 
     283    * @param {String} path           Request path 
     284    * @param {Object} options        Request ooptions 
     285    * @config {String} method        HTTP method 
     286    * @return {Object} Returns the result object from Route.match() 
    257287    */    
    258288   this.recognizePath = function(path, options) { 
    259       return recognizePath(path, options, controller); 
     289      return recognizePath(this, path, options); 
    260290   }, 
    261291 
    262292   /** @ignore */ 
    263293   this.generate = function(options, req) { 
    264       return generate(options, req || global.req, controller); 
     294      return generate(this, options, req); 
    265295   }, 
    266296 
    267297   /** 
    268     * Returns all routes. 
    269     * @return {array} Routes 
    270     */ 
    271    this.getAll = function() { 
    272       return routes; 
    273    } 
    274  
    275    /** 
    276298    * Return a single route at index. 
    277     * @param {number} index   Index starting at 0 (zero) 
    278     * @return {object} Route 
     299    * @param {Number} index   Index starting at 0 (zero) 
     300    * @return {Route} A single Route object. 
    279301    */ 
    280302   this.get = function(index) { 
     
    288310      routes = []; 
    289311   } 
    290  
    291    /** @ignore */  
     312    
     313   this.toString = function() { 
     314      return routes.invoke('toString').join("\n"); 
     315   } 
     316 
     317   /** 
     318    * Returns all routes. 
     319    * @return {Array} Array of Route objects 
     320    */ 
     321   this.__defineGetter__("routes", function() { 
     322      return routes; 
     323   }) 
     324 
     325   /** 
     326    * Returns the short name of the controller. 
     327    * @return {String} 
     328    */ 
    292329   this.__defineGetter__("controller", function() { 
    293       return controller
     330      return controllerName
    294331   }) 
     332    
     333   /** 
     334    * Holds the number of routes. To be compatible with Arrays. 
     335    * @return {Number}     
     336    */  
     337   this.__defineGetter__("length", function() { 
     338      return routes.length; 
     339   })    
    295340 
    296341} 
     342 
     343/** 
     344 * Global RouteSet to be used for root. // ?? 
     345 * ?? That's the rails way, but i'm not sure if we should keep that,  
     346 * because routes are mounted at controllers. 
     347 */ 
     348Routes = new RouteSet(); 
    297349 
    298350 
     
    302354 * options go to the file overview. 
    303355 *  
    304  * @param {object} options 
    305  * @config {string} pattern          
     356 * @param {Object} options 
     357 * @config {String} pattern          
    306358 *   Pattern to match against the incoming request, starting with a leading slash. 
    307359 *   A variable is identified by a laeding $ (dollar-sign) for example '/$controller/$action'. 
    308  * @config {string} [action]          Name of the action. Will be used as default if the pattern doesn't contain an '$action' 
    309  * @config {string} [controller]      Controller object name as string, for example "BlogController".  
    310  * @config {object} [controller]      Controller object, for example BlogController. 
    311  * @config {string} [forwardTo]        
     360 * @config {String} [action]          Name of the action. Will be used as default if the pattern doesn't contain an '$action' 
     361 * @config {String} [controller]      Controller object name as string, for example "BlogController".  
     362 * @config {Object} [controller]      Controller object, for example BlogController. 
     363 * @config {String} [forwardTo]        
    312364 *    Controller object name as string to which the route should forward, if the incoming request path 
    313365 *    does match the pattern, but is longer than the pattern. 
    314  * @config {object} [forwardTo]       Controller as obect to be used for forwardTo. 
    315  * @config {object} [defaults]         
     366 * @config {Object} [forwardTo]       Controller as obect to be used for forwardTo. 
     367 * @config {Object} [defaults]         
    316368 *    Object containing default values to be used, if the incoming request path doesn't provide the  
    317369 *    information becuase it's to short. 
    318  * @config {object} [requirements] 
     370 * @config {Object} [requirements] 
    319371 *    Contains regular expressions, that will be tested against the resulting routes named values. 
    320372 *    For example requirements.id will be tested against route.param.id when performing Route.match(). 
    321  * @config {object} [conditions] 
     373 * @config {Object} [conditions] 
    322374 *    Each condition will be called on Named object of functions that will be called to perform extra tests. The following attributes will be 
    323375 *    passed to the function call: 
     
    330382 * @see draw  
    331383 */ 
    332 var Route = function(options) { 
     384var Route = function(controllerName, options) { 
    333385 
    334386   var VARIABLE_IDENTIFIER = "$"; 
     
    338390   var route = this; 
    339391 
     392   this.controllerName = controllerName || "root"; 
    340393   this.options = options; 
     394   this.originalOptions = Object.clone(options); 
     395   delete this.originalOptions.pattern; 
    341396   this.pattern = options.pattern; 
    342397   this.action = options.action; 
    343    this.controller = options.controller; 
    344398 
    345399   // detect HTTP method 
     
    365419   this.options = options || {}; 
    366420   this.defaults = Object.extend({ 
    367       controller : options.controller || "root", 
    368421      action : options.action || "index", 
    369422      id : null 
     
    393446      return route.components[idx].substr(VARIABLE_IDENTIFIER.length); 
    394447   }; 
    395     
    396    var getNameForController = function(controller) { 
    397       return (typeof controller === "string") ? controller : controller.getShortName(); 
    398    } 
    399     
    400    var getControllerFromName = function(name) { 
    401       var controller = name; 
    402       if (typeof controller === "string") controller = controller.endsWith("Controller") ? global[controller] : global[controller.capitalize().dasherize().camelize() + "Controller"]; 
    403       if (typeof controller === "object" && controller.instance) controller = controller.instance(); 
    404       return controller; 
    405    } 
    406  
    407448 
    408449   /** 
     
    434475    * <dt><i>string</i> controller</dt> 
    435476    *   <dd>The matching part from the path for $controller. Otherwise it will default to 'root'</dd>     
    436     * @param {string} [req]      Request object, or something that is similar to a request object. 
    437     * @config {object} data      Hash containing request parameters with their values. 
    438     * @config {string} method    HTTP-Method in uppercase letters. May also be ANY. 
    439     * @param {string} path       Relative path of the incoming request, starting with a leading slash (/path/to/something). 
    440     * @return {object} 
     477    * @param {String} [req]      Request object, or something that is similar to a request object. 
     478    * @config {Object} data      Hash containing request parameters with their values. 
     479    * @config {String} method    HTTP-Method in uppercase letters. May also be ANY. 
     480    * @param {String} path       Relative path of the incoming request, starting with a leading slash (/path/to/something). 
     481    * @return {Object} 
    441482    *  The result object is a clone of this route augmented by the following properties:  
    442483    *  params, method, doForward, extension, format, path, request, action, controller. 
     
    511552      result.request = req; 
    512553      result.action = result.params["action"] || "index"; 
    513       result.controller = getControllerFromName(result.params["controller"] || "root")
     554      result.controllerName = result.params["controller"] || this.controllerName
    514555      result.route = this; 
    515556      for (var name in result.params) { 
     
    520561      req.remainingPath = result.remainingPath; 
    521562      result.toString = function() { 
    522          var r = "{"; 
    523          for (var i in this) { 
    524             r += i + ":"; 
    525             switch (typeof this[i]) { 
    526                case "undefined": 
    527                   r += "undefined"; 
    528                   break; 
    529                case "function": 
    530                   r += "function"; 
    531                   break; 
    532                default: 
    533                   r += this[i].toString() 
    534             } 
    535             r += "," 
    536          } 
    537          return r.substr(0, r.length-1) + "}"; 
     563         return uneval(this.params); 
    538564      } 
    539565 
     
    541567       * Result object from the method Route.match() 
    542568       * @see Route.match 
    543        * @param {object} params Object filled with values for each variable for this route pattern. For example an incoming request with the path "/say/hello" matching against a route with the pattern "/$controller/$action/$id" will result in a params object -> {controller:"say", action: "hello", id: null} 
     569       * @param {Object} params Object filled with values for each variable for this route pattern. For example an incoming request with the path "/say/hello" matching against a route with the pattern "/$controller/$action/$id" will result in a params object -> {controller:"say", action: "hello", id: null} 
    544570       * @param {string) method HTTP-Method in uppercase letters. May also be ANY. 
    545571       */ 
     
    547573   }; 
    548574 
    549    this.getPath = function(controller, options, req) { 
     575   this.getPath = function(options, req) { 
     576      logger.debug("this.getPath("+uneval(options)+","+uneval(req)+") for " + this.toString()) 
    550577      var result = ""; 
    551       var req = req || global.req; 
    552       var controller = getControllerFromName(options.controller || req.data.controller || controller); 
    553       options = options || {}; 
    554       delete options.controller; 
    555  
     578      var req = Object.extend({ 
     579         method : "GET", 
     580         path : "/", 
     581         data : {} 
     </