[Helma-user] helma summer camp

Anton Pirker helma at gmiatlich.net
Wed Jul 19 01:04:25 CEST 2006


Hi List!

Here are my notes from day two and day three of the Helma Summer Camp.
Thank's again to Michi for organizing this workshop!

Hopefully the notes will provide a little help to Helma-Newbies.


x) HobObject get persist when they are added to a persisten collection 
of HopObjects. (or with a call of the .persist() method of the object)

x) Changes to an HopObject are stored in the database at the end of an 
request.

x) always use forms with method="post"
use req.isPost() to check if a form was submitted. (so you don't need to 
know the name of the submit-button)


x) javascript syntax:

param.action || "main"
-> if param.action exists (is not NULL or "") take param.action, else 
take "main"

param.action && param.action.toUpperCase()
-> if param.action exists (is not NULL or "") run the 
param.action.toUpperCase(), else break



x) very handy: the generic list_macro() in HopObject-Prototype:

function list_macro(param) {
    var coll = param.collection ? this[param.collection] : then;
    for(var i=0; i<coll.count(); i++) {
        var obj = coll.get(i);
        obj.renderSkin(param.skin || "defaulSkin");
    }
}

so you can make one macro wich displays all of your lists. you can 
implement pagination an other list-specific stuff here.

you can call it like: <% this.list collection="users" skin="listView" %>


x) sometimes it is handy to map the Root-Prototype to the DB. this is 
useful if you store settings in Root.


x) _parent = organization.bla, root.bla
-> the second parent is a fall back, if there is no organization, than 
the prototype is mapped to root.

x) M:N: checking for subscription

The M:N-Setup:

--subscription/type.prototype--
    user         = object(User)
    user.local   = user_f_id
    user.foreign = user_id
    magazine         = object(Magazine)
    magazine.local   = magazine_f_id
    magazine.foreign = magazine_id
--end--

--user/type.prototype--
    subscriptions            = collection(Subscription)
    subscriptions.local      = user_id
    subscriptions.foreign    = user_f_id
    subscriptions.accessname = magazine_f_id
--end--   

--magazine/type.prototype--
    subscriptions            = collection(Subscription)
    subscriptions.local      = magazine_id
    subscriptions.foreign    = magazine_f_id
    subscriptions.accessname = user_f_id
--end--   

The trick is to use the foreign-key of the other table as the accessname 
for the collection.
So you don't need to loop through all subscriptions of a user to see if 
she subscribes to a specific magazine.
You just need to call:

theUser.subscriptions.get(theMagazine._id.toString());

The .toString() is importand, because if you call .get(n) where n is an 
integer, than the n-th element will be returned.
If you call .get("n") than the element with the accessname "n" will be 
returned.


x) Don't put persistent Objects into the Session!
If you really need this just put the Object-ID into the Session and get 
the Object with the stored ID
every time you need it.

x) onRequest() function of an HopObject is always executed before the 
action.
onRequest() in the HobObject-Prototype is a good place for 
security-stuff. For example checking for logged in user like this:

function onRequest() {
    if(!session.user && (req.action != "login" || req.action != 
"register")) {
        res.redirect(root.href("login"));       
    }
}

the check for req.action is necessary because if you no user is logged 
in than you will be redirected
to the login and before you can login the onRequest ist called again.

x) HobObject-Prototypes have static methods like .getById(). So you 
don't need a collection to get a Object.
For example: var invoice = Invoice.getById("20060815")

x) Group by:

A collection with a .group definition is a collection of collections.
Every group is represented through a collection.
Lets assume we have a table with a date (in format yyyy-mm-dd), and we 
group it by this date in a collection
called "allElements"

Now we can call
var todaysElements = allElements.get("2006-07-19");
to get all elements of the group with todays date. If there aren't any 
elements in that group todaysElements
will be NULL

x) nice separation of controller and model [1] made easy in helma:
   (this also describes how nice helma actions could look like)

--actions.js--
    function register_action() {
        if(req.isPost()) {
            try {
                var u = this.doRegister(req.data);
                session.login(u);
                redirect(root.href());
            } catch(err) {
                res.message = err.message;
            }       
        }
       
        renderSkin("register");
    }
--end--

--function.js--
    function doRegister(dat) {
        //register the user with the infos stored in 'dat'.
        if(problem)
            throw new Error("there was an quite unexpected error!");
    }
--end--

never do the data-manipulation stuff in the action (the controller).
all that stuff belongs in extra functions (to the model).
the session handling should be in the controller. so you can use the 
doRegister() function
for bulk-registration without problems.

[1] http://en.wikipedia.org/wiki/Model_View_Controller
    http://de.wikipedia.org/wiki/Model_View_Controller
   
   
x) Every macro has prefix and suffix.
<% this.questions prefix="<li>" suffix="/li>" %>

x) Javascript tips:
format(str):  \n -> <br />
stripTags(str): removes the html-tags from str.

x) macro handler:

one can call also the parent of the current object.
<% parentname.dateOfBirth %>

x) pushing parameters to skins only if it is absolutly neccesary!
for example when using alternate background-colors for lists:

for(var i=0; elements.count(); i++) {
    var p = new Object();
    p.className = "element" + (i%2);
   
    var obj = elements.get(i);
    obj.renderSkin("listElement", p);
}

--the skin--
    <div class="<% param.className %>"><% this.name %></div>
--end--
   
--the css stylesheet--
    .element0 {
        background-color: black;
    }
   
    .element1 {
        background-color: red;
    }   
--end--
 
x) res.handlers is quite powerful!

lets assume we are in an order. an order has an customer.
to access this customer from the macro we create a handler for it:

res.handlers.customer = this.customer   
this.renderSkin("detailView");

--the skin--
    <% this.date %> - <% this.orderNum %><br />
    <% customer.name %><br />
    <% customer.address %>
    ...
--end--

this makes all this
    function customerName_macro() {
        return this.customer.name;
    }
stuff obsolete!


x) again group-by:

--type.properties--
    foo.group           = answer
    foo.group.prototype = Answer
--end--

Scripted Prototypes! I can add an Answer Prototype and it can have
own actions and functions like every other prototype.


x) Perfomance hint: Indices on foreign keys!



to, that's it!
now you can start over, falling in love with Helma!

Anton








More information about the Helma-user mailing list