Deinhammer, Guido wrote:
Hi,

I followed the discussion here on Macros - and thought you might be
interested in what I have done to overcome most of their shortcomings.

As mostly a casual observer here, I found it fascinating. My initial reaction, though, is that, pragmatically speaking, does this make any sense? I mean, Velocity's main competitor in this space, a project that is largely (but far from completely) my fault, has had all these features you need in its stable releases for over 5 years. What's more, this is a pretty well known project we're talking about. FreeMarker. It's used directly by a lot of people but also as a third-party component for templating in a large number of tools and frameworks out there, Struts 2, Hibernate Tools, Netbeans.... So these features have been stress-tested a huge amount in the course of those 5 years, and, while I'd be the last person to say it's perfect, there is a test of time that has happened, and you can say that this is something pretty solid that you could built mission-critical aps on top of. It will take you a long time to get the same features into that state working from the current state of Velocity.

So, you know, to add these new features to Velocity might only be a moderate amount of work, (you'd probably spend more hours on all the community debate over how everything has to work than actually implementing it) but what you have will not be nearly as robust as the same features in FreeMarker that have been part of the stable releases for 5 years. I don't think there is any way you can subject your work to the test that is comparable to 5 years of real world usage by a fairly large community out there.

Another aspect of things is that FreeMarker is still something of a moving target. We're going to have a prerelease of 2.4 which has some interesting new features, which, I grant, will not be stress-tested than the ones that have been in there for so long. But I mean, to say, that by the time you bring Velocity up to the feature set of FreeMarker of 5 years ago, surely we'll have a fairly stable 2.4 out with some interesting new features and you'lll be quite well behind again.

I don't know how you take this note, Guido, I can tell you that I don't have any axe to grind with you personally. I think it's great that you have fire in your belly to some work in this application space. It would be great if you wanted to be a collaborator in our project. In that case, you'd at least be working approximately on the cutting edge of what is available in this kind of tool, rather than trying to catch up with the state of an existing tool of 5 years ago....

You might take this as something of a rebuke but it's not really: I think you would be better off, before investing all this time to add features to Velocity or some other existing tool, to look around out there and get a sense of what the state of the art in that space is. In this case, you would see that the features you need that you intended to implement in Velocity already exist in a very stable form in an alternative tool. It's not a rebuke, because, I can imagine you were eager to get into this and hack the code, and you saw that you could add these features and so on. But that's not always pragmatic. It makes sense to check whether you're not reinventing the wheel.



I understand my writing turned out a little lengthy, but if you are
using macros, it should be worth the read...

Basically I am working on a web-based UI and we use velocity to render
html. We have a number of fairly complex macros - some are recursive -
and when I did a check on the memory consumption that finally made me
rethink whether velocity is suited to what we do at all. The problem is
that it includes the parsed nodetree of a macro wherever it is called
(https://issues.apache.org/jira/browse/VELOCITY-223).

Uhh, yeah, that's the kind of thing I was referring to in another note when I mentioned first order implementation mistakes... I mean, seriously, is this the kind of thing one should see in such a visible project that is over 7 years old?

In our project
that resulted in up to 10MB of heap memory for a screen and we have
around 800 of them. This would mean that we can cache only around 15
screens in the ResourceCache.
This finally made me change all our (mostly generated) velocity
templates.
Before that there were a few other inconveniences that we got used to -
parameter passing is not ideal - we have often well over 20 parameters
many of them can be defaulted, so the normal macro parameters are not
very useful for us - but that is easy enough to work around - just pass
a single map to your macro - and inside the macro you can use a Java
tool or a user directive to default the values in the argument map with
values of a second - defaultValue map.

Yeah, this has been in FreeMarker for 5 or 6 years. See:

http://freemarker.org/docs/dgui_misc_userdefdir.html#autoid_22

and

http://freemarker.org/docs/ref_directive_macro.html

The macro system allows default parameters to be specified as well as vararg style lists where the extra args are passed in as a variable that's a list.

That made the maros a lot easier to use for us - but the memory problem
remains.
Another problem was that the caller cannot pass a body, or velocity
markup to the macro - that would be very convenient if you want to
implement e.g. a collapsible section

Uh, yeah, see:

http://freemarker.org/docs/dgui_misc_userdefdir.html#autoid_23

and again,

http://freemarker.org/docs/ref_directive_macro.html

(https://issues.apache.org/jira/browse/VELOCITY-558 or
https://issues.apache.org/jira/browse/VELOCITY-583)

Uhh, yeah, BTW, I think the reporter of issue 583 gave up on Velocity and is now using FreeMarker.... Path of least resistance, as the other tool already had the feature in a highly stable form.


Other's reported using a variable for the name of the macro doesn't
work.

I think FreeMarker has had this for 6 years or so.

<#assign myMacro = .vars[macroName]>
<@myMacro />

It looks in the special built-in .vars hash to find the variable associated with the string in macroName and the resulting expression is a macro. It can be assigned to a variable like myMacro and invoked, as in the second line, or you could just write it in slightly more obfuscated style in one line:

<@.vars[macroName]/>

This isn't possible in Velocity because of some, you know, first order implementation mistakes AFAICS.


So I thought instead of using a macro call I could just implement a
directive that renders another .vm file with the same context and the
same Writer - similar to parse but with the option of passing
parameters. So that's what I did.

Yeah, I see the motivation but it's still a bit of a hack. It means that you need a separate file for each macro, doesn't it? I really think it's easier to use a template engine that simply allows for this usage pattern as part of its core feature set.


And with the three directives I have
attached you can do something like this:

#call($myMacro {"arg1": true, "arg2":false})

In FreeMarker, (and I'm assuming that $myMacro contains the macro to be invoked by name) that's:

<#assign macroToInvoke = .vars[myMacro]>
<@macroToInvoke arg1=true arg2=false />

and again, it could be done in one line with:

<@.vars[myMacro] arg1=true arg2=false />



The $myMacro variable has to resolve to a fileName (you can set the
directory for this, so you don't have to give the full path, and you can
skip the .vm ending). The second parameter is a map that is added to the
context under the name "args" - the "macro" can access the parameters
with $args.arg1.

One drawback, which is the reason you use a map here is is that you're any parameters to main overall context. In FreeMarker, it works as it should sensibly, the arg1 and arg2 are local, only defined in the body of the macro.

The "macro" itself, is nothing but a .vm file

If you use macros extensively you're going to have a lot of separate files. IMO, it makes more sense to be able to group related macros in the same file.


You can also do this:

#callBlock(collapsibleSection {"isCollapsed":$collapsed, "title":"My
Section"})
 My section content.....
#end



Where your collapsibleSection.vm file could look like this:

> #callBlock(collapsibleSection {"isCollapsed":$collapsed, "title":"My
> Section"})
> My section content.....
> #end


I think the FreeMarker syntax is potentially simpler. There's none of this args.title stuff or having to put things in a separate file and all that. You'd write the macro as:

<#macro collapsibleSection isCollapsed, title>
  <table><th><td>${title}</td></th>
   <#if !isCollapsed>
     <#nested>
   </#if>
   </table>
</#macro>


<@collapsibleSection collapsed "My Section">
   My section content
</@>

or in more verbose form:

<@collapsibleSection isCollapsed=collapsed title="My Section">
   My section content
</@collapsibleSection>


Where the #content() directive will just render the body of the
callBlock directive - in this case " My section content.....".

Uh huh, yeah,... revolutionary! ;-)



So have a look at the attached code, if you have similar problems with
macros - it is quite short and should be easy to understand.
Maybe something like this should be added for the 1.6 version - or the
whole macro concept could be converted to a solution like that - leaving
the existing syntax intact.

The idea (though hardly new :-)) is of course quite right and the ability to do this kind of thing really should be considered very basic to a template engine, particularly when you're using it to generate HTML or similar markup. The problem, I think, is that if you compare it to a competing template engine where this functionality has been implemented as part of the core, you've got a lot of rather awkward syntax, and strange artificial rules, like putting the macro in its own text file and referring to the arguments as args.argName and all that. Also, you're not declaring the names (or number of) parameters that the macro takes, so using a set of macros written this way is going to be significantly more error-prone. FreeMarker will say, "Hey, I expected 2 args to macro collapsibleSection and only got 1" or whatever. Especially given the flakey way that Velocity handles undefined or missing values, (See: http://in.relation.to/Bloggers/AStoryAboutFreeMarkerAndVelocity ) this could make things all rather error-prone.

Anyway, it is good to see someone who wants to give back and I hope my comments are useful to you.

Jonathan Revusky
--
lead developer, FreeMarker project, http://freemarker.org/

Velocity or FreeMarker: Looking at 5 Years of Practical Experience
http://freemarker.blogspot.com/2007/12/velocity-of-freemarker-looking-at-5.html




Best regards,

Guido Deinhammer

Btw: we have also added another cool directive that allows partial page
rendering...but I didn't have time to write it up yet...


------------------------------------------------------------------------

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to