Before we will walk through Helma's functionality in detail, we give a quick, very compressed overview over some of its basic mechanism, and introduce a couple of Helma specific terms.
Prototypes are being defined by adding new directories to
[AppDir] (directory name = prototype
name), their functions by adding (arbitrarily named) js-files
containing function definitions into these directories.
Properties only need to be declared within
if they are going to be mapped on a relational database. Then a
property corresponds to specific column in that database
In addition to this basic mapping, it is also possible to build object models and define relations between prototypes. A prototype property can be a reference to another prototype, a prototype can contain collections/lists of other prototypes, and it is possible (and highly recommended) to define an object hierarchy for these prototypes.
The Root prototype is a special prototype that is always present and immediately persistent as soon as the application is started. It represents the root of the object model hierarchy.
The DB mapping and object modelling are defined in files
need to reside in the according prototype directories. Besides
these mappings, Helma developers (generally) do not have to mess
around with SQL. Any creating, retrieving, modifying and removing
of HopObjects happen within the scripting scope, with Helma
taking care of the building and execution of all the necessary
Besides having prototype specific methods, which all require the existence of a HopObject (i.e. no static methods can be defined), it is possible to define methods in a global context, which can be directly called from anywhere in the application code.
There are four ways of how a function call can be initiated:
a web request via HTTP
a XmlRpc request
an internal function call by the scheduler
an external function call via helma.main.launcher.Commandline
with the first type naturally being the most common one for a web application.
If a browser/client makes a web request to an application, Helma tries to resolve the request path from left to right, walking down the object hierarchy (starting with root) and trying to find an according web accessible function. A prototype function is web accessible if and only if its function name ends with '_action'. This means that a request to http://localhost:8080/appname/main is handled by the function main_action of the prototype Root. A request to http://localhost:8080/appname/latestStories/437/show (i.e. the request path handled by the application is '/latestStories/437/show') will have Helma fetch the root object, fetch a collection named 'latestStories' attached to the root (if defined), fetch a HopObject (supposedly of a prototype named 'Story') with ID 437 (if exists), and call function show_action (if defined) on that HopObject. That action will subsequently write to the response buffer, which is then send back to the client as soon as the function returns. Any parameter that is sent together with the request, no matter whether that is a URL parameter (?key=value), or an element of a submitted POST-form or a Cookie can be read via the Object req.data.
Aside from writing directly to the response, via the function res.write, Helma provides a powerful and clean rendering framework, consisting of so called skins and macros. Skins are basically just parts of the response, usually being HTML code, mixed with (tightly controlled) calls of certain functions, the macros. These skins are either defined in a global context, or for a specific prototype. By default skins are expected to be file-based, stored with the file-extension '.skin' in the according prototype directory, but can also be fetched from a database or any other data source. A macro is simply a global or prototype function with its name ending with '_macro'. These macros are being replaced with some other string/text, usually in dependence on certain conditions, when that skin is being rendered.