r9537 - in helma-ng/trunk: apps/demo apps/storage modules modules/helma modules/test/core modules/test/helma src/org/helma/javascript

[email protected]
Newsgroups gmane.comp.java.helma.cvs
Message-ID <20090223120115.A99353D0D6@mia>
Author: hannes
Date: 2009-02-23 13:01:15 +0100 (Mon, 23 Feb 2009)
New Revision: 9537

Modified:
   helma-ng/trunk/apps/demo/actions.js
   helma-ng/trunk/apps/demo/config.js
   helma-ng/trunk/apps/demo/webmodule.js
   helma-ng/trunk/apps/storage/config.js
   helma-ng/trunk/apps/storage/main.js
   helma-ng/trunk/modules/global.js
   helma-ng/trunk/modules/helma/webapp.js
   helma-ng/trunk/modules/test/core/array_test.js
   helma-ng/trunk/modules/test/core/object_test.js
   helma-ng/trunk/modules/test/helma/file_test.js
   helma-ng/trunk/modules/test/helma/skin_test.js
   helma-ng/trunk/modules/test/helma/unittest_test.js
   helma-ng/trunk/src/org/helma/javascript/ModuleScope.java
   helma-ng/trunk/src/org/helma/javascript/ReloadableScript.java
   helma-ng/trunk/src/org/helma/javascript/RhinoEngine.java
Log:
Implement module exports as defined in ServerJS Securable Modules proposal

https://wiki.mozilla.org/ServerJS/Modules/SecurableModules

This means that require() no longer returns the module scope itself but the exports
object, containing only the module's exported properties. The global export() is now
implemented on top of this and continues to work as before. As a consequence, export
definitions no longer only apply to include() but also require() and import(), and an
ommitted export causes the exported property not to be available at all.

This means explicit exports are now necessary where they weren't before: in the app's
config module, in webapp modules defining action functions, and in test case modules.

As a little reward in return, actions no longer need the _action suffix as only
functions that are explicitly exported are invokable as actions.

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

Modified: helma-ng/trunk/apps/demo/actions.js
===================================================================
--- helma-ng/trunk/apps/demo/actions.js	2009-02-23 12:01:12 UTC (rev 9536)
+++ helma-ng/trunk/apps/demo/actions.js	2009-02-23 12:01:15 UTC (rev 9537)
@@ -2,8 +2,10 @@
 
 var log = helma.logging.getLogger(__name__);
 
+export('index', 'extra_path', 'skins', 'logging', 'continuation');
+
 // the main action is invoked for http://localhost:8080/
-function index_action(req, res) {
+function index(req, res) {
     res.render('skins/index.html', { title: 'Welcome to Helma NG' });
     res.debug(req.cookies.toSource());
     res.debug(req.params.toSource());
@@ -12,14 +14,14 @@
 
 // additional path elements are passed to the action as arguments,
 // e.g. /extra.path/foo/bar
-function extra_path_action(req, res) {
+function extra_path(req, res) {
     for (var i = 2; i < arguments.length; i++) {
         res.write(arguments[i], "<br/>");
     }
 }
 
 // demo for skins, macros, filters
-function skins_action(req, res) {
+function skins(req, res) {
     res.render('skins/skins.html', {
         title: 'Skin Demo',
         name: 'Luisa',
@@ -28,7 +30,7 @@
 }
 
 // demo for log4j logging
-function logging_action(req, res) {
+function logging(req, res) {
     if (req.params.info) {
         log.info("Hello world!");
     } else if (req.params.error) {
@@ -42,7 +44,7 @@
 }
 
 // demo for continuation support
-function continuation_action(req, res) {
+function continuation(req, res) {
 
     // local data - this is the data that is shared between resuming and suspension
     var data = {};

Modified: helma-ng/trunk/apps/demo/config.js
===================================================================
--- helma-ng/trunk/apps/demo/config.js	2009-02-23 12:01:12 UTC (rev 9536)
+++ helma-ng/trunk/apps/demo/config.js	2009-02-23 12:01:15 UTC (rev 9537)
@@ -1,16 +1,16 @@
-var httpConfig = {
+exports.httpConfig = {
   staticDir: 'static'
 };
 
-var urls = [
+exports.urls = [
     [ /^mount\/point/, 'webmodule' ],
     [ /^/, 'actions' ],
 ];
 
-var middleware = [
+exports.middleware = [
     'helma.webapp.continuation',
     'helma.logging'
 ];
 
-var charset = 'utf8';
-var contentType = 'text/html';
\ No newline at end of file
+exports.charset = 'utf8';
+exports.contentType = 'text/html';

Modified: helma-ng/trunk/apps/demo/webmodule.js
===================================================================
--- helma-ng/trunk/apps/demo/webmodule.js	2009-02-23 12:01:12 UTC (rev 9536)
+++ helma-ng/trunk/apps/demo/webmodule.js	2009-02-23 12:01:15 UTC (rev 9537)
@@ -1,6 +1,8 @@
 // a simple web app/module
 
-function index_action(req, res) {
+export('index');
+
+function index(req, res) {
     var context = {
         title: 'Module Demo',
         href: req.path

Modified: helma-ng/trunk/apps/storage/config.js
===================================================================
--- helma-ng/trunk/apps/storage/config.js	2009-02-23 12:01:12 UTC (rev 9536)
+++ helma-ng/trunk/apps/storage/config.js	2009-02-23 12:01:15 UTC (rev 9537)
@@ -1,10 +1,10 @@
-var urls = [
+exports.urls = [
     [ /^$/, 'main' ]
 ];
 
-var middleware = [
+exports.middleware = [
     "helma.logging"
 ]
 
-var charset = 'utf8';
-var contentType = 'text/html';
\ No newline at end of file
+exports.charset = 'utf8';
+exports.contentType = 'text/html';
\ No newline at end of file

Modified: helma-ng/trunk/apps/storage/main.js
===================================================================
--- helma-ng/trunk/apps/storage/main.js	2009-02-23 12:01:12 UTC (rev 9536)
+++ helma-ng/trunk/apps/storage/main.js	2009-02-23 12:01:15 UTC (rev 9537)
@@ -1,9 +1,11 @@
 import('helma.webapp', 'webapp');
 import('model');
 
+export('index');
+
 // the main action is invoked for http://localhost:8080/
 // this also shows simple skin rendering
-function index_action(req, res) {
+function index(req, res) {
     if (req.params.save) {
         createBook(req, res);
     }

Modified: helma-ng/trunk/modules/global.js
===================================================================
--- helma-ng/trunk/modules/global.js	2009-02-23 12:01:12 UTC (rev 9536)
+++ helma-ng/trunk/modules/global.js	2009-02-23 12:01:15 UTC (rev 9537)
@@ -13,7 +13,24 @@
      */
     Object.defineProperty(this, "require", {
         value: function(moduleName) {
-            return getRhinoEngine().loadModule(getRhinoContext(), moduleName, this);
+            var module = getRhinoEngine().loadModule(getRhinoContext(), moduleName, this);
+            var exports = module.exports;
+            if (!exports || !(exports instanceof Object)) {
+                exports = {};
+                Object.defineProperty(module, "exports", { value: exports });
+            }
+            // the __exports__ array is an alternative way for a module to export properties.
+            // it contains a list of property names that will be copied to the exports object.
+            if (module.__exports__) {
+                for each (var key in module.__exports__) {
+                    // print("Exporting", moduleName, key, "->", typeof(module[key]));
+                    Object.defineProperty(exports, key, {
+                        value: module[key],
+                        enumerable: true
+                    });
+                }
+            }
+            return exports; 
         }
     });
 
@@ -48,13 +65,7 @@
     Object.defineProperty(this, "include", {
         value: function(moduleName) {
             var module = this.require(moduleName);
-            var exported = module.__export__;
-            if (!exported) {
-                throw ReferenceError("Property __export__ is not defined on module " + moduleName);
-            } else if (!(exported instanceof Array)) {
-                throw TypeError("Property __export__ is not an array in module " + moduleName);
-            }
-            for each (var key in exported) {
+            for (var key in module) {
                 this[key] = module[key];
             }
         }
@@ -66,12 +77,12 @@
      */
     Object.defineProperty(this, "export", {
         value: function() {
-            var list = this.__export__ || [];
+            var list = this.__exports__ || [];
             for (var i = 0; i < arguments.length; i++) {
                 list.push(arguments[i]);
             }
-            if (this.__export__ != list) {
-                Object.defineProperty(this, "__export__", { value: list });
+            if (this.__exports__ != list) {
+                Object.defineProperty(this, "__exports__", { value: list });
             }
         }
     });

Modified: helma-ng/trunk/modules/helma/webapp.js
===================================================================
--- helma-ng/trunk/modules/helma/webapp.js	2009-02-23 12:01:12 UTC (rev 9536)
+++ helma-ng/trunk/modules/helma/webapp.js	2009-02-23 12:01:15 UTC (rev 9537)
@@ -66,7 +66,7 @@
 
     function getAction(module, name) {
         name = name || "index";
-        var action = module[name.replace(/\./g, "_") + "_action"];
+        var action = module[name.replace(/\./g, "_")];
         if (typeof action == "function") {
             return action;
         }

Modified: helma-ng/trunk/modules/test/core/array_test.js
===================================================================
--- helma-ng/trunk/modules/test/core/array_test.js	2009-02-23 12:01:12 UTC (rev 9536)
+++ helma-ng/trunk/modules/test/core/array_test.js	2009-02-23 12:01:15 UTC (rev 9537)
@@ -3,9 +3,10 @@
 var logging = require("helma.logging");
 var log = logging.getLogger(__name__);
 
+export('testCase');
+
 var testCase = new TestCase("core.array");
 
-
 // test data
 var empty = [];
 

Modified: helma-ng/trunk/modules/test/core/object_test.js
===================================================================
--- helma-ng/trunk/modules/test/core/object_test.js	2009-02-23 12:01:12 UTC (rev 9536)
+++ helma-ng/trunk/modules/test/core/object_test.js	2009-02-23 12:01:15 UTC (rev 9537)
@@ -1,9 +1,11 @@
 include('helma.unittest');
 require('core.object');
 
-var c = new TestCase('core.object');
+export('testCase');
 
-c.testMerge = function() {
+var testCase = new TestCase('core.object');
+
+testCase.testMerge = function() {
     var x = {a: 1, b: 2};
     var y = {b: 3, c: 4};
     var z = {c: 5, d: 6};

Modified: helma-ng/trunk/modules/test/helma/file_test.js
===================================================================
--- helma-ng/trunk/modules/test/helma/file_test.js	2009-02-23 12:01:12 UTC (rev 9536)
+++ helma-ng/trunk/modules/test/helma/file_test.js	2009-02-23 12:01:15 UTC (rev 9537)
@@ -2,6 +2,8 @@
 var {File} = require('helma.file');
 require('core.string');
 
+export('testCase');
+
 var testCase = new TestCase('helma.file');
 
 var filename = 'helma_file_test_' + String.random(10);

Modified: helma-ng/trunk/modules/test/helma/skin_test.js
===================================================================
--- helma-ng/trunk/modules/test/helma/skin_test.js	2009-02-23 12:01:12 UTC (rev 9536)
+++ helma-ng/trunk/modules/test/helma/skin_test.js	2009-02-23 12:01:15 UTC (rev 9537)
@@ -1,33 +1,35 @@
 include('helma.unittest');
 include('helma.skin');
 
-var c = new TestCase('helma.skin');
+export('testCase');
 
-c.testBasic = function() {
+var testCase = new TestCase('helma.skin');
+
+testCase.testBasic = function() {
     var skin = createSkin('simple');
     assertEqual('simple', render(skin));
     assertEqual('simple', render(skin, {}));
 };
 
-c.testValue = function() {
+testCase.testValue = function() {
     var skin = createSkin('before <% value %> after');
     var context = {value: 'HERE'};
     assertEqual('before HERE after', render(skin, context));
 };
 
-c.testMacro = function() {
+testCase.testMacro = function() {
     var skin = createSkin('before <% macro %> after');
     var context = {macro_macro: function () 'HERE'};
     assertEqual('before HERE after', render(skin, context));
 };
 
-c.testFilter = function() {
+testCase.testFilter = function() {
     var skin = createSkin('before <% x | filter %> after');
     var context = {filter_filter: function (str) 'HERE'};
     assertEqual('before HERE after', render(skin, context));
 };
 
-c.testSubskin = function() {
+testCase.testSubskin = function() {
     var skin;
     var context;
 
@@ -42,7 +44,7 @@
     assertEqual('ab', render(skin, context));
 };
 
-c.testSubskinWhitespace = function() {
+testCase.testSubskinWhitespace = function() {
     var skin;
 
     skin = createSkin('a\n<% subskin sub %>\nb\n');

Modified: helma-ng/trunk/modules/test/helma/unittest_test.js
===================================================================
--- helma-ng/trunk/modules/test/helma/unittest_test.js	2009-02-23 12:01:12 UTC (rev 9536)
+++ helma-ng/trunk/modules/test/helma/unittest_test.js	2009-02-23 12:01:15 UTC (rev 9537)
@@ -1,5 +1,7 @@
 include("helma.unittest");
 
+export('testCase');
+
 var testCase = new TestCase("helma.unittest");
 
 testCase.testAssertTrue = function() {

Modified: helma-ng/trunk/src/org/helma/javascript/ModuleScope.java
===================================================================
--- helma-ng/trunk/src/org/helma/javascript/ModuleScope.java	2009-02-23 12:01:12 UTC (rev 9536)
+++ helma-ng/trunk/src/org/helma/javascript/ModuleScope.java	2009-02-23 12:01:15 UTC (rev 9537)
@@ -21,6 +21,7 @@
 import org.mozilla.javascript.Context;
 import org.mozilla.javascript.NativeObject;
 import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.NativeArray;
 
 /**
  * A scriptable object that keeps track of the resource it has been loaded from
@@ -32,15 +33,19 @@
     Repository repository;
     String name;
     long checksum;
+    Scriptable exports;
     private static final long serialVersionUID = -2409425841990094897L;
 
-    public ModuleScope(String moduleName, Trackable source, Scriptable prototype) {
+    public ModuleScope(String moduleName, Trackable source, Scriptable prototype, Context cx) {
+        setParentScope(null);
+        setPrototype(prototype);
         this.source = source;
         this.repository = source instanceof Repository ?
                 (Repository) source : source.getParentRepository();
         this.name = moduleName;
-        setParentScope(null);
-        setPrototype(prototype);
+        this.exports = cx.newObject(this);
+        defineProperty("exports", exports,  DONTENUM | READONLY | PERMANENT);
+        defineProperty("__exports__", cx.newArray(this, 0), DONTENUM);
         defineProperty("__name__", moduleName, DONTENUM);
         defineProperty("__path__", source.getPath(), DONTENUM);
     }
@@ -49,6 +54,11 @@
         return repository;
     }
 
+    public void reset(Context cx) {
+        delete("__shared__");     
+        defineProperty("__exports__", cx.newArray(this, 0), DONTENUM);
+    }
+
     public long getChecksum() {
         return checksum;
     }
@@ -61,6 +71,10 @@
         return name;
     }
 
+    public Scriptable getExports() {
+        return exports;
+    }
+
     @Override
     public String toString() {
         return "[ModuleScope " + source + "]";

Modified: helma-ng/trunk/src/org/helma/javascript/ReloadableScript.java
===================================================================
--- helma-ng/trunk/src/org/helma/javascript/ReloadableScript.java	2009-02-23 12:01:12 UTC (rev 9536)
+++ helma-ng/trunk/src/org/helma/javascript/ReloadableScript.java	2009-02-23 12:01:15 UTC (rev 9537)
@@ -179,7 +179,7 @@
     /**
      * Get a module scope loaded with this script
      *
-     * @param prototype the parent scope for the module
+     * @param prototype the prototype for the module, usually the shared top level scope
      * @param moduleName the module name
      * @param cx the rhino context
      * @return a new module scope
@@ -201,9 +201,9 @@
                 modules.put(source, module);
                 return module;
             }
-            module.delete("__shared__");
+            module.reset(cx);
         } else {
-            module = new ModuleScope(moduleName, source, prototype);
+            module = new ModuleScope(moduleName, source, prototype, cx);
         }
         modules.put(source, module);
         script.exec(cx, module);

Modified: helma-ng/trunk/src/org/helma/javascript/RhinoEngine.java
===================================================================
--- helma-ng/trunk/src/org/helma/javascript/RhinoEngine.java	2009-02-23 12:01:12 UTC (rev 9536)
+++ helma-ng/trunk/src/org/helma/javascript/RhinoEngine.java	2009-02-23 12:01:15 UTC (rev 9537)
@@ -144,7 +144,7 @@
             }
             ReloadableScript script = new ReloadableScript(resource, this);
             scripts.put(resource, script);
-            mainScope = new ModuleScope("__main__", resource, topLevelScope);
+            mainScope = new ModuleScope("__main__", resource, topLevelScope, cx);
             retval = evaluate(cx, script, mainScope);
         	if (retval instanceof Wrapper) {
         		return ((Wrapper) retval).unwrap();
@@ -209,7 +209,7 @@
             Repository repository = repositories.get(0);
             Resource resource = repository.getResource("<shell>");
             Scriptable parentScope = mainScope != null ? mainScope : topLevelScope;
-            ModuleScope scope = new ModuleScope("<shell>", resource, parentScope);
+            ModuleScope scope = new ModuleScope("<shell>", resource, parentScope, cx);
             try {
                 evaluate(cx, getScript("helma.shell"), scope);
             } catch (Exception x) {
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.