Helma Logo
main list history
previous version  overview  next version

Version 16 by Lehni on 17. December 2005, 07:10

== 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 ====Comments

== Juerg LehniLehni ==

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.
- * "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?
- * 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.
- * If foreach is implemented as a JavaScript macro, how could this notation be possible: (% foreach comment in (story.comments)?
- * 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 {{ }}  %)
- * 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.

     removed
     added