Helma Logo
main list history
previous version  overview  next version

Version 19 by hannes on 19. December 2005, 12:20

== Spec ==

* # Macros in skins are delimited by (% %).
* # Macros can contain both named and unnamed parameters, in any combination (see Notes for implementation details).
* # Named parameters are denoted as name=value, unnamed parameters are denoted as value.
* # For string parameter values, single or double quotes are optional unless the value contains whitespace characters.
* # Macro parameter values wrapped in ( ) are interpreted as nested macros.
* # Macro parameter values wrappedn in {{ }} are interpreted as nested skins.
* # Skins and macros are both implemented as plain JS objects.
* # Nested skins and macros are passed to macros as skin and macro objects, respectively. In other words, macro evaluation and skin rendering is up to the containing macro.
* # Standard macros such as if/else, foreach, equals, greaterThan are implemented as plain macros in JS.

== Notes ==

* My original intention was to use * instead of % for top level macro delimiters, but Gobi would interpret these as Wiki links. Anyway, I haven't committed myself to any choice of delimiter sequences, I just think my current proposal looks pretty sane and implementable to me. I do think this is an essential question.
* Even though it doesn't look that way, this proposal provides easy backwards compatibility with old style model domain macro syntax and conventions. I'll show how when I talk further about JS implementation.

Macro parameters are passed in a way that allows sequential value lookup, sequential name lookup and named lookup:

    var param = {
      values: [],
      names: [],
      byName: {}
    }

== Examples ==

==== Simple login info/form

(%
  if (user) {{
    <div>Logged in as (% user.name %)</div>
  }} else {{
    <form action="(% loginUrl %)">
    <table><tr>
      <td>Name</td><td><input name="name"></td>
    </tr><tr>
      <td>Password</td><td><input type="password" name="password"></td>
    </tr></table>
    </form>
  }}
%)

==== Story with comments

Variant 1, using multiple skin/macro nesting:

<h1>(% story.title %)</h1>
<div>(% story.text %)</div>
(%
  if (story.hasComments) {{
  <table>
  (% foreach comment in (story.comments)
    {{
    <tr>
      <td>(% encode(comment.title) %)</td>
      <td>(% encode(comment.text) %)</td>
    </tr>
    }}
  %)
  </table>
  }}
%)

Variant 2, using prefix and suffix parameters to avoid nesting:

<h1>(% story.title %)</h1>
<div>(% story.text %)</div>
(%
  foreach comment in (story.comments)
  {{
    <tr>
      <td>(% encode(comment.title) %)</td>
      <td>(% encode(comment.text) %)</td>
    </tr>
  }}
  prefix=<table> suffix=</table>
%)

== Comments ==

==== Juerg Lehni

I find this proposal somehow unclear and not so straight forward, but maybe I'm just missunderstanding it. Here are some remarks:

* You are using commment.title, comment.text, user.name in your examples, and I'm not sure if these are simply properties of these objects or if they're actually defined as macros. I would assume the second is the case. <div class="reply">I would think in these cases that these are just plain properties. I think direct property access will be enhanced in Helma 2 from what is possible in Helma 1. But strictly speaking, this is not part of this proposal, which is about template/skin syntax.</div> -hns</div>
* "Macro parameter values wrapped in ( ) are interpreted as nested macros": Again: I assume you're talking about parameters such as comment.title. But what if I'd want to call encode with a constant string as parameter?parameter? <div class="reply">For a constant string, you'd just write it as-is, or wrapped in quotes (see point 4 in the spec)</div>
* Macros / Nested Skins: I understand that you want to try to avoid the mixing code / content scenario, but i somehow find the current proposal even more confusing than the previously discussed nesting of "code" and content blocks. The problem is that there are two kinds of open / close "tags" now ( "(%", "{{" ), and it seems to become rather hard to keep the overview. <div class="reply">I think this is the better approach. Most template engines just have tags to mark the beginning and ending of code sections, but they have no way of marking embedded skins and code. Having real embedded skins and macros is much preferable to me.</div>
* If foreach is implemented as a JavaScript macro, how could this notation be possible: (% foreach comment in (story.comments)?comments)?<div class="reply"><tt>foreach</tt> is the macro name, <tt>comment</tt> and <tt>in</tt> ar both constant unnamed string parameters, <tt>story.comments</tt> is a embedded macro.</div>
* Likewise, how is it possible to have two macros with each a nested skin inside one macro block, and the macros' execution depending on each other. That would be the case with a construct like (% if {{ }} else {{ }}  %)%)<div class="reply"><tt>if</tt> is the macro name, the first and third parameters are nested skins, the second parameter is constant string "else".</div>
* prefix / suffix: While it's nice to have this in the foreach example because it removes the need for checking for hasComments, I think it just increases complexity and makes the result harder to read.<div class="reply">I think it does in some cases, but it makes things simpler in others. I do think it's a good thing to keep, not just because it's part of Helma's heritage.</div>

     removed
     added