Modules and Scopes
If a module wants to make use of another module, it needs to import the other module. Helma NG provides two global functions for this, one to import a module into a discrete namespace, and another to import some or all properties of a module into its own scope.
Now the special thing about Helma NG is that the two modules (one doing the import, the other being imported) are fully isolated from each other. The imported module does not have any way at all to access the importing module, and the importing module can only access the imported one via the namespace into which it imported it, or the properties and functions it explicitly imported from it. And what's more, you don't have to do anything special to accomplish this. You just write scripts with global functions and properties and never worry about stuff that is defined in other scripts.
The way Helma NG does this is by giving each Module its own top-level scope.
Module names are derived from a module's file name by removing the ".js" extension and using "/" as path item separator. Thus, a module residing in file helma/http.js can be referred to as 'helma/http'. When a module is loaded, Helma NG defines a property called
__name__ and sets it to the module name.
Helma NG uses a module path to resolve module names. By default, the module path consists of the application directory and the modules directory in the Helma NG installation. It is possible to add directories and zip archives to the module path using the HELMA_MODULE_PATH environment variable or command line arguments to the Helma NG runtime and shell. When asked to import a module, Helma walks along the module path until it finds a module matching the name. If no matching module can be found, an error is thrown.
import() function loads a module into a module scope and creates a reference in the caller's scope pointing to the imported scope. The imported module's name is used as property name in the calling module. For instance, the following statement will load the
helma/http module and add it as
helma.http to the scope of the calling module.
require() function loads a module into a module scope and returns the scope object to the caller.
var http = require('helma/http');
Another way of using the functionality of a module is to add one or more properties or functions from the module to one's own top level scope. For example, you could use the following statement to add the Client constructor to your local module's top level scope:
If a function imported using include() wants to access properties or functions of its importing scope, it can do by either accepting the scope as an argument to the function, or by using the
this keyword, which contains the object the function was called on. In fact, the render() function in module helma/skin uses this to load a skin resource from a location relative to the importing module's file rather than itself.
By default, Helma NG creates each module once per request. This means that if a module is loaded from several other modules in the same application, you be sure that you deal with the actual same module instance and scope throughout each request, but are isolated from other requests that may be running at the same time.
Sometimes it is desirable to have a module instantiated only once, and shared among all requests. You can achieve this by defining a property called
__shared__ in your module and giving it the value
var __shared__ = true;
This will result in your module scope to be kept alive across and shared among requests.