[Hypercontent-users] New features: improved dependency recognition and more power to Velocity
Brought to you by:
alexvigdor
|
From: Alex V. <al...@bi...> - 2007-11-13 15:51:28
|
Hello all,
I've just checked in some small but powerful changes for
HyperContent in CVS. First of all, there is one new jar file
dependency, "commons-lang" from the apache jakarta project, which
I've brought in specifically to enrich the functionality available to
Velocity templates. I know most HC users are templating in XSL, but
as you read this email you might begin to see some uses for Velocity
as well! The following commons util classes are now available in the
default velocity context:
baseContext.put("random-string-utils", new RandomStringUtils());
baseContext.put("string-escape-utils", new StringEscapeUtils());
baseContext.put("string-utils", new StringUtils());
baseContext.put("word-utils", new WordUtils());
baseContext.put("number-utils", new NumberUtils());
baseContext.put("random-utils", new RandomUtils());
baseContext.put("date-format-utils", new DateFormatUtils());
baseContext.put("date-utils", new DateUtils());
The other change I've checked in enables a whole new level of
flexibility and ease of development by adding dependency recognition
for use of the "document()" function in XSL and the corresponding
"resource-loader" functions available in Velocity. This is most
easily explained by an example, which I'll base on an issue I know
affects at least one major HC site.
Suppose you have a number of standard disclaimers that appear
throughout a site, that you need to embed arbitrarily within the page
content. Rather than copy and paste the text into each page, you set
up a directory with all the standard disclaimers as markup
fragments. Then you add a field to your XML DTD that allows the user
to insert just the path of a standard disclaimer within another
page. WIthin your XSL you make a document() call to dynamically
resolve that disclaimer and insert it in the page output.
This has always worked, but what did not work was dependency
recognition - if you made a change to the standard disclaimer, the
page output would not automatically be refreshed. The workaround for
this was to essentially add all the disclaimers as XML includes for
every page, just to force a dependency so the pages will update when
the disclaimers update. Of course this means that every page will
refresh any time any disclaimer is changed - pure brute force.
With the new dependency support for document() calls, this brute
force workaround is no longer necessary. As long as the document()
call resolves to either a file in the HC repository, or to the output
of another HC pipeline, the dependency is now automatically
recognized (document() calls to external URLs are not recognized as
dependencies). This makes document() a full-featured alternative to
using HC XML includes for single lookups. The document function
still does not offer the wildcard resolution or external URL
dependency support that XML includes have, and heavily used XML
includes will be somewhat more efficient than using document() since
HC caches SAX event streams for includes, but for many purposes you
may find you can use fewer includes.
I'll provide one more example now of how these two new features open
up some interesting uses for Velocity. Suppose you want to build a
site with a nice asynchronous, dynamic Web 2.0 feeling, but you want
to template the fragments using XSL and you want to use vanilla
Javascript to avoid cross-domain security restrictions on AJAX
requests. How can you get the markup back to the browser inside a
javascript call? Here's an easy solution using Velocity with the
commons StringEscapeUtils.
<xml-doctype path="/**/index.xml" root="page" definition="/config/
page.dtd" label="Page">
<output content-type="text/html">
<transform source="/config/page.xsl"/>
</output>
<output content-type="text/javascript">
<exec stage="org.hypercontent.project.engine.stage.VelocityStage">
<with-param name="source" value="/config/escape-js.vm"/>
</exec>
</output>
</xml-doctype>
And in /config/escape-js.vm just two lines:
#set($val = $resource-loader.loadAssetToString($strings.concat
($originBase,'.html')))
setMarkup('$request-path','${string-escape-utils.escapeJavaScript
($val)}');
---------------------------------------------------------
Translation:
Define an HTML output for the page using /config/page.xsl to
transform the source into the desired markup.
Define a JS output for the page that takes the HTML output and
escapes it into a javascript call that might come back to the browser
something like this:
setMarkup('/index.js','<html>Here\'s the output from page.xsl</html>');
---------------------------------------------------------
Now, obviously you would need a JS framework in the browser that
would define the behavior of the setMarkup function, but that's all
there is on the HC side to enable this use case.
I developed and tested these techniques for a new site that I'm
working on, but I also verified with local copies of a number of
existing sites that this does not break anything. The code changes
were quite unobtrusive and isolated to a handful of classes, but of
course I recommend that you test thoroughly in a non-production
environment before deploying the latest code. If you have any
questions or feedback, let me know!
Cheers,
Alex
|