r9639 - in helma-ng/trunk: apps/demo apps/filestore apps/googlestore modules/helma modules/helma/webapp

[email protected] Tue, 21 Apr 2009 16:01:10 +0200 (CEST)
Newsgroups gmane.comp.java.helma.cvs
Message-ID <20090421140110.E303D3D0D6@mia>
Author: hannes
Date: 2009-04-21 16:01:10 +0200 (Tue, 21 Apr 2009)
New Revision: 9639

Modified:
   helma-ng/trunk/apps/demo/actions.js
   helma-ng/trunk/apps/demo/webmodule.js
   helma-ng/trunk/apps/filestore/main.js
   helma-ng/trunk/apps/googlestore/main.js
   helma-ng/trunk/modules/helma/buffer.js
   helma-ng/trunk/modules/helma/webapp.js
   helma-ng/trunk/modules/helma/webapp/response.js
Log:
Move to jack-based action handling in helma/webapp: actions are called with a single request argument and expected to return a response object. Add convenience Response constructors such as SkinnedResponse and RedirectResponse. Disable middleware for the time being.

Details at http://dev.helma.org/trac/helma/changeset/9639

Modified: helma-ng/trunk/apps/demo/actions.js
===================================================================
--- helma-ng/trunk/apps/demo/actions.js	2009-04-21 14:01:08 UTC (rev 9638)
+++ helma-ng/trunk/apps/demo/actions.js	2009-04-21 14:01:10 UTC (rev 9639)
@@ -1,26 +1,25 @@
 import('helma/logging');
+include('helma/webapp/response');
 
 var log = helma.logging.getLogger(__name__);
 
 export('index', 'extra_path', 'skins', 'logging', 'continuation');
 
 // the main action is invoked for http://localhost:8080/
-function index(req, res) {
-    res.render('skins/index.html', { title: 'Welcome to Helma NG' });
-    // res.debug(req.cookies.toSource());
-    // res.debug(req.params.toSource());
+function index(req) {
+    return new SkinnedResponse('skins/index.html', { title: 'Welcome to Helma NG' });
 }
 
 
 // additional path elements are passed to the action as arguments,
 // e.g. /extra.path/2008/09
-function extra_path(req, res, year, month) {
-    res.write("Extra arguments: ", year, month);
+function extra_path(req, year, month) {
+    return new Response("Extra arguments:", year, month);
 }
 
 // demo for skins, macros, filters
-function skins(req, res) {
-    res.render('skins/skins.html', {
+function skins(req) {
+    return new SkinnedResponse('skins/skins.html', {
         title: 'Skin Demo',
         name: 'Luisa',
         names: ['Benni', 'Emma', 'Luca', 'Selma']
@@ -28,7 +27,7 @@
 }
 
 // demo for log4j logging
-function logging(req, res) {
+function logging(req) {
     if (req.params.info) {
         log.info("Hello world!");
     } else if (req.params.error) {
@@ -38,18 +37,17 @@
             log.error(e, e.rhinoException);
         }
     }
-    res.render('skins/logging.html', { title: "Logging Demo" });
+    return new SkinnedResponse('skins/logging.html', { title: "Logging Demo" });
 }
 
 // demo for continuation support
 function continuation(req, res) {
 
-    res.render('skins/continuation.html', {
+    return new SkinnedResponse('skins/continuation.html', {
         title: "Continuations",
         skin: "start",
         note: "NOTE: Continuation support is currently broken, so I have disabled this demo for the time being."
     });
-	return;
 
     // local data - this is the data that is shared between resuming and suspension
     var data = {};

Modified: helma-ng/trunk/apps/demo/webmodule.js
===================================================================
--- helma-ng/trunk/apps/demo/webmodule.js	2009-04-21 14:01:08 UTC (rev 9638)
+++ helma-ng/trunk/apps/demo/webmodule.js	2009-04-21 14:01:10 UTC (rev 9639)
@@ -1,4 +1,5 @@
 // a simple web app/module
+include('helma/webapp/response');
 
 export('index');
 
@@ -7,5 +8,5 @@
         title: 'Module Demo',
         href: req.path
     };
-    res.render('skins/modules.html', context);
+    return new SkinnedResponse('skins/modules.html', context);
 }

Modified: helma-ng/trunk/apps/filestore/main.js
===================================================================
--- helma-ng/trunk/apps/filestore/main.js	2009-04-21 14:01:08 UTC (rev 9638)
+++ helma-ng/trunk/apps/filestore/main.js	2009-04-21 14:01:10 UTC (rev 9639)
@@ -1,21 +1,21 @@
-import('helma/webapp', 'webapp');
-import('model');
+include('helma/webapp/response');
+include('model');
 
 export('index');
 
 // the main action is invoked for http://localhost:8080/
 // this also shows simple skin rendering
-function index(req, res) {
+function index(req) {
     if (req.params.save) {
-        createBook(req, res);
+        return createBook(req);
     }
     if (req.params.remove) {
-        removeBook(req, res);
+        return removeBook(req);
     }
-    res.render('skins/index.html', {
+    return SkinnedResponse('skins/index.html', {
         title: 'Storage Demo',
         books: function(/*tag, skin, context*/) {
-            var books = model.Book.all();
+            var books = Book.all();
             return books.map(function(book) {
                 return book.getFullTitle() + ' ' + getDeleteLink(book);
             }).join('<br>\r\n');
@@ -23,19 +23,19 @@
     });
 }
 
-function createBook(req, res) {
-    var author = new model.Author({name: req.params.author});
-    var book = new model.Book({author: author, title: req.params.title});
+function createBook(req) {
+    var author = new Author({name: req.params.author});
+    var book = new Book({author: author, title: req.params.title});
     // author is saved transitively
     book.save();
-    res.redirect('/');
+    return new RedirectResponse('/');
 }
 
-function removeBook(req, res) {
-    var book = model.Book.get(req.params.remove);
+function removeBook(req) {
+    var book = Book.get(req.params.remove);
     // author is removed through cascading delete
     book.remove();
-    res.redirect('/');
+    return new RedirectResponse('/');
 }
 
 function getDeleteLink(book) {
@@ -43,5 +43,5 @@
 }
 
 if (__name__ == "__main__") {
-    webapp.start();
+    require('helma/webapp').start();
 }

Modified: helma-ng/trunk/apps/googlestore/main.js
===================================================================
--- helma-ng/trunk/apps/googlestore/main.js	2009-04-21 14:01:08 UTC (rev 9638)
+++ helma-ng/trunk/apps/googlestore/main.js	2009-04-21 14:01:10 UTC (rev 9639)
@@ -1,3 +1,4 @@
+include('helma/webapp/response');
 include('model');
 
 export('index');
@@ -4,14 +5,14 @@
 
 // the main action is invoked for http://localhost:8080/
 // this also shows simple skin rendering
-function index(req, res) {
+function index(req) {
     if (req.params.save) {
-        createBook(req, res);
+        return createBook(req);
     }
     if (req.params.remove) {
-        removeBook(req, res);
+        return removeBook(req);
     }
-    res.render('skins/index.html', {
+    return new SkinnedResponse('skins/index.html', {
         title: 'Storage Demo',
         books: function(/*tag, skin, context*/) {
             return Book.all().map(function(book) {
@@ -21,20 +22,20 @@
     });
 }
 
-function createBook(req, res) {
+function createBook(req) {
     var author = new Author({name: req.params.author});
     author.save(); // no cascading save yet
     var book = new Book({author: author, title: req.params.title});
     book.save();
-    res.redirect('/');
+    return new RedirectResponse('/');
 }
 
-function removeBook(req, res) {
+function removeBook(req) {
     var book = Book.get(req.params.remove);
     // no cascading delete
     book.author.remove();
     book.remove();
-    res.redirect('/');
+    return new RedirectResponse('/');
 }
 
 function getDeleteLink(book) {

Modified: helma-ng/trunk/modules/helma/buffer.js
===================================================================
--- helma-ng/trunk/modules/helma/buffer.js	2009-04-21 14:01:08 UTC (rev 9638)
+++ helma-ng/trunk/modules/helma/buffer.js	2009-04-21 14:01:10 UTC (rev 9639)
@@ -31,6 +31,10 @@
         return content.join('');
     };
 
+    this.forEach = function(fn) {
+        content.forEach(fn);
+    }
+
     // hack
     this.toFirebugConsole = function() {
         var enc = JSON.stringify(this.toString());

Modified: helma-ng/trunk/modules/helma/webapp/response.js
===================================================================
--- helma-ng/trunk/modules/helma/webapp/response.js	2009-04-21 14:01:08 UTC (rev 9638)
+++ helma-ng/trunk/modules/helma/webapp/response.js	2009-04-21 14:01:10 UTC (rev 9639)
@@ -1,31 +1,35 @@
+include('hashp');
 include('helma/buffer');
 import('helma/system', 'system');
 
-export('Response');
+export('Response', 'SkinnedResponse', 'RedirectResponse' /*, 'NotFoundResponse', 'ServerErrorResponse'*/);
 
-// FIXME hack to get this to evaluate
-function Response(servletResponse) {
+function Response() {
 
-    var writer;
     var status = 200;
+    var charset;
+    var contentType;
+    var headers = {};
+    var buffer = new Buffer();
 
     Object.defineProperty(this, 'write', {
         value: function write() {
-            writer = writer || servletResponse.getWriter();
             var length = arguments.length;
             for (var i = 0; i < length; i++) {
-                writer.write(String(arguments[i]));
+                buffer.write(String(arguments[i]));
                 if (i < length - 1)
-                    writer.write(' ');
+                    buffer.write(' ');
             }
             return this;
         }
     });
 
+    this.write.apply(this, arguments);
+
     Object.defineProperty(this, 'writeln', {
         value: function writeln() {
             this.write.apply(this, arguments);
-            this.write('\r\n');
+            buffer.write('\r\n');
             return this;
         }
     });
@@ -33,14 +37,14 @@
 
     /**
      * Render a skin to the response's buffer
-     * @param skin
-     * @param context
-     * @param scope
+     * @param skin path to skin resource
+     * @param context context object
+     * @param scope optional scope for relative resource paths
      */
     Object.defineProperty(this, 'render', {
         value: function render(skin, context, scope) {
             var render = require('helma/skin').render;
-            this.write(render(skin, context, scope));
+            buffer.write(render(skin, context, scope));
         }
     });
 
@@ -79,28 +83,27 @@
     });
 
     Object.defineProperty(this, 'redirect', {
-        value: function(target) {
-            servletResponse.sendRedirect(target);
-            // fixme: temporary solution until webapp refactoring
-            throw {redirect: target};
+        value: function(location) {
+            status = 303;
+            HashP.set(headers, 'Location', String(location));
         }
     });
 
     Object.defineProperty(this, 'charset', {
         get: function() {
-            return servletResponse.getCharacterEncoding();
+            return charset;
         },
-        set: function(charset) {
-            servletResponse.setCharacterEncoding(charset);
+        set: function(c) {
+            charset = c;
         }
     });
 
     Object.defineProperty(this, 'contentType', {
         get: function() {
-            return servletResponse.getContentType();
+            return contentType;
         },
-        set: function(contentType) {
-            servletResponse.setContentType(contentType);
+        set: function(c) {
+            contentType = c;
         }
     });
 
@@ -110,8 +113,41 @@
         },
         set: function(s) {
             status = s;
-            servletResponse.setStatus(s);
         }
     });
 
+    Object.defineProperty(this, 'getHeader', {
+        value: function(key) {
+            HashP.get(headers, String(key));
+        }
+    });
+
+    Object.defineProperty(this, 'setHeader', {
+        value: function(key, value) {
+            HashP.set(headers, String(key), String(value));
+        }
+    });
+
+    Object.defineProperty(this, 'close', {
+        value: function() {
+            if (charset) {
+                contentType = contentType || HashP.get('content-type') || "text/html";
+                contentType += "; charset=" + charset;
+            }
+            if (contentType) {
+                HashP.set(headers, "Content-Type", contentType);
+            }
+            return [status, headers, buffer];
+        }
+    })
+
 }
+
+function SkinnedResponse(skin, context, scope) {
+    var render = require('helma/skin').render;
+    return [200, {}, render(skin, context, scope)];
+}
+
+function RedirectResponse(location) {
+    return [303, {Location: location}, "See other: " + location];
+}

Modified: helma-ng/trunk/modules/helma/webapp.js
===================================================================
--- helma-ng/trunk/modules/helma/webapp.js	2009-04-21 14:01:08 UTC (rev 9638)
+++ helma-ng/trunk/modules/helma/webapp.js	2009-04-21 14:01:10 UTC (rev 9639)
@@ -30,17 +30,17 @@
     if (log.debugEnabled) log.debug('got config: ' + config.toSource());
 
     var req = new Request(env['jack.servlet_request']);
-    var res = new Response(env['jack.servlet_response']);
+    var res;
 
-    req.charset = res.charset = config.charset || 'utf8';
-    res.contentType = config.contentType || 'text/html';
+    req.charset = config.charset || 'utf8';
+    // res.contentType = config.contentType || 'text/html';
 
     // invoke onRequest
-    invokeMiddleware('onRequest', config.middleware, [req, res]);
+    // invokeMiddleware('onRequest', config.middleware, [req, res]);
     // resume continuation?
-    if (continuation.resume(req, res)) {
+    /* if (continuation.resume(req, res)) {
         return;
-    }
+    } */
 
     // resolve path and invoke action
     var path = req.path;
@@ -96,31 +96,35 @@
                     //split
                     path = path.split(/\/+/);
                     var action = getAction(module, path[0]);
-                    if (typeof action == "function" && path.length < action.length) {
+                    if (typeof action == "function" && path.length <= action.length) {
                         // add remaining path elements as additional action arguments
                         var actionArgs = path.slice(1).map(decodeURIComponent);
-                        var args = [req, res].concat(actionArgs);
-                        invokeMiddleware('onAction',
+                        var args = [req].concat(actionArgs);
+                        /* invokeMiddleware('onAction',
                                 config.middleware,
-                                [req, res, action, actionArgs]);
-                        action.apply(module, args);
-                        return;
+                                [req, action, actionArgs]); */
+                        res = action.apply(module, args);
                     }
                     break;
                 }
             }
         }
-        notfound(req, res);
     } catch (e) {
         if (e.retry) {
             throw e;
         } else if (!e.redirect) {
-            invokeMiddleware('onError', config.middleware, [req, res, e]);
-            error(req, res, e);
+            // invokeMiddleware('onError', config.middleware, [req, res, e]);
+            res = error(req, e);
         }
     } finally {
-        invokeMiddleware('onResponse', config.middleware, [req, res]);
+        // TODO
+        if (!res)
+            res = notfound(req);
+        if (!(res instanceof Array) && res.close)
+            res = res.close();
+        // invokeMiddleware('onResponse', config.middleware, [req, res]);
     }
+    return res;
 }
 
 function invokeMiddleware(hook, middleware, args) {
@@ -146,7 +150,8 @@
  * Standard error page
  * @param e the error that happened
  */
-function error(req, res, e) {
+function error(req, e) {
+    var res = new Response();
     res.status = 500;
     res.contentType = 'text/html';
     res.writeln('<h2>', e, '</h2>');
@@ -165,18 +170,19 @@
     } else {
         log.error(e.toString());
     }
-    return null;
+    return res.close();
 }
 
 /**
  * Standard notfound page
  */
-function notfound(req, res) {
+function notfound(req) {
+    var res = new Response();
     res.status = 404;
     res.contentType = 'text/html';
     res.writeln('<h1>Not Found</h1>');
     res.writeln('The requested URL', req.path, 'was not found on the server.');
-    return null;
+    return res.close();
 }
 
 /**