> I'm actually quite interested in this sort of thing and I
> wrote a blog
> article about it. You might or might not find it interesting. Other
> people might (or might not) find it interesting as well:
>
> http://freemarker.blogspot.com/2006/07/designerdeveloper-division-of-labor.html
>
> There, you see that, I make no bones about what I think regarding
> Velocity. It definitely seems to me that VTL is lacking certain basic
> features that you would need to build reusable components. The macro
> system is just too deficient.
>
Jonathan,
I would agree with you that macro support in Velocity is not sufficient for
constructing what might be called a "modular" or "component"-based system. As
far as I am aware, it was ever intended for that purpose. Personally, I regard
macro functionality as being more akin to what might be termed "snippets".
[for those who are interested in "modular" or "component"-based templating
systems, read on ... ;D If not, you've probably stopped reading already anyway.]
For our usage of Velocity, I recommend against the use of macros for all but
the most simple use cases. I do not do so because of limitations in Velocity
macro support but because using this feature for the creation of reusable
"components" would imply a level of scripting which I would argue is not
optimally achieved within a templating language. All this talk of "macros" is a
red herring, IMHO. Hence, we are not utilizing macros at all for the modular
system I referred to in my original e-mail.
A display component is, for all intents and purposes, a domain object (or a
facade upon one) some properties of which one wishes to expose for display
purposes and, potentially, to decorate with additional display-specific
properties. Additionally, a display component is an object the display context
of which one requires to be able to detect and subsequently act accordingly
thereupon. Consequently, I would suggest that the required functionality of a
"component framework" extends to the following:
1. Obtaining an instance of a domain object
2. Determining the context in which that instance is being invoked
Neither of these requirements, I would argue, fall within the remit of a
template language. This is why we are building our system in a manner that is
entirely decoupled from Velocity.
Why mention it on the Velocity list? Because we _do_ want to render components
that this system is capable of creating instances of. Therefore, we have built
a Velocity client (basically, a Velocity directive ... and we made a tool for
the hell of it - because it was so simple to do). This client obtains a
component object instance, discovers the contextual display-related data (e.g.
template name etc that the framework loads on the basis of the request) and
renders it in-situ at the point the directive resides in the template.
At this point one might think "Hey, isn't that a built like a view-layer
'pulling' data and isn't that A Bad Thing?"
Well, yes and no (to both parts of that question).
What the above enables us to do is to have something like the following:
--- in the main "view" template ---
#component("MyComponent", {"id":$myComponent.id}) ## the webapp is still
responsible for pushing the id of the component you want to display into the
view (amongst other data, in all probability)
--- end ---
... but we can have an entirely independent "component template" that makes the
most of the beautiful simplicity of VTL and simply displays the properties of
an object (info about which the template authors can obtain from javadoc)!
--- component template ("MyComponent.vm") ---
<h1>${this.heading}</h1>
<p>${this.text}</p>
--- end ---
Additionally, because we have access to contextual information about where the
component is being displayed, we can (for example) easily get the Velocity
client to use different templates based upon that data (in practice, this
happens transparently on the basis of conventions)
However, because we have not written our components as VTL or Freemarker
macros, we can easily go ahead and make alternative clients (e.g. a JSON client
... or even a web services client using a RESTful or SOAP api ... or even a
FreeMarker client! ;D) which re-use the exact same components.
But doesn't that mean having to create loads of java classes and restart the
server (which would be a pain in the arse)?
Again, yes and no. If a component has to implement complex functionality, then
yes: it requires implementation in Java - but that would be the appropriate
place to implement such functionality.
On the other hand, one can simply use a named "dynamic component" to utilize
the contextual data and rendering functionality available by way of the
Velocity client in templates; e.g.
--- in the main "view" template ---
#component("DynamicComponent", {"name":"MyDynamicComponent"}) ## this basically
making a map the property names and values for which are determined contextually
--- end ---
template is not quite as simple, but remains far more readable than some
horrific macro spaghetti ...
--- component template ("MyDynamicComponent.vm") ---
<h2>${this.get("heading")}</h2>
<p>${this.get("text")}<p>
--- end ---
> Large, complex, modular sites using Velocity, eh? I suppose it's
> possible. But really, you know, when you can't even #parse a set of
> commonly used macros in a separate file, and there's no notion of
> scoping or namespaces whatsoever, so that any variable
> defined locally
> in a macro potentially clobbers variables defined elsewhere
> -- to rely
> on that kind of tool to build something complex and modular, does not
> seem like a very good technical decision. The tool simply lacks
> necessary things for modularity.
>
It's more than possible: we've already done it even without the formalized
support provided by the "framework" I describe above (which is really just a
bit of hackery on top of Spring) being in place - and we are by no means alone
in that (rather simple) achievement.
Nonetheless, I think it would be foolishness to try and assert that Velocity is
in some way "pure and flawless". Proper whitespace handling would be a
requirement in my book (and, yes, I know FreeMarker has this already).
So why didn't we just make a FreeMarker client and use that?
1. Because we were already using Velocity (I know there is a template
conversion tool, but there are other considerations such as familiarity of
client-side developers with current systems, skills investment etc)
2. (And this is the main reason) Because Velocity explicitly does not attempt
to be a solution which bundles everything and the kitchen sink. The clear
delineation of responsibility (basically, taking a context+template and
rendering) combined with clear extension points and an elegant syntax made it
ideal for our purposes.
>
> >
> > There might be some common ground covered between us and
> The Guardian here
> > which could be fed back into the Velocity project itself, perhaps?
>
> Well, historically, lobbying Velocity developers for features
> that you
> need has not been a very fruitful path. I won't go on further about
> that, but surely you can perceive that, even bending over
> backwards to
> be generous and all, you can't describe this as a very dynamic
> environment, can you?
>
From my reasoning above, you may have guessed that I am not lobbying for new
features. Far from it: the restriction to a clearly delineated requirement is a
virtue that Velocity possesses. I would never want it to come bundled with it's
own "component framework", for example.
"Fed back into the Velocity project" might imply a couple of things:
1. Us potentially making available our framework code and Velocity client but
as an entirely separate entity (in the same way that Malcolm and the Click
project might be thought to "feed back" into the Velocity project).
2. The possibility of simply "donating" the code such that it might be
maintained as a sub-project were we to decide against maintaining our
development as an open source project (which is not a commitment to be taken on
lightly).
Obviously, I cannot speak for The Guardian in any way on this subject and
merely mentioned them in this context in case they were watching ;D
Best wishes,
Chris
********************************************************************************
DISCLAIMER: This e-mail is confidential and should not be used by anyone who is
not the original intended recipient. If you have received this e-mail in error
please inform the sender and delete it from your mailbox or any other storage
mechanism. Neither Macmillan Publishers Limited nor any of its agents accept
liability for any statements made which are clearly the sender's own and not
expressly made on behalf of Macmillan Publishers Limited or one of its agents.
Please note that neither Macmillan Publishers Limited nor any of its agents
accept any responsibility for viruses that may be contained in this e-mail or
its attachments and it is your responsibility to scan the e-mail and
attachments (if any). No contracts may be concluded on behalf of Macmillan
Publishers Limited or its agents by means of e-mail communication. Macmillan
Publishers Limited Registered in England and Wales with registered number
785998
Registered Office Brunel Road, Houndmills, Basingstoke RG21 6XS
********************************************************************************