[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 |