Macro bug fixes / enhancements
(edit: now fixed in svn head for Velocity 1.6. leaving this note up until Velocity 1.6 is released. Will)
some notes by Will <email@example.com>.
I'm starting to look into the remain bug fix/enhancements for the 1.5 release. Not surprisingly, many of the remaining issues are all pretty subtle. I thought I'd publically share a few notes on macros.
Here's a catalog of the open macro-related issues targeted for 1.5.
http://issues.apache.org/jira/browse/VELOCITY-277 Macros in #parsed files are not refreshed when including page is refreshed
http://issues.apache.org/jira/browse/VELOCITY-362 Can't load macros in file loaded with #parse
http://issues.apache.org/jira/browse/VELOCITY-146 Macros not evaluated on the first attempt with #parse
http://issues.apache.org/jira/browse/VELOCITY-24 Calls to local macros not always made when template caching is off
http://issues.apache.org/jira/browse/VELOCITY-82 VM libs will not autoreload if unparseable at Velocity startup
Most of these issues stem from how Velocity stores macros. Macros can be stored either at a global level or on a per-template level. This is controlled by the config key "velocimacro.permissions.allow.inline.local.scope", which is false by default. (i.e. all macros are part of global scope). See the developer's guide.
This means that the following common-sense use case (referenced in VELOCITY-277, VELOCITY-362, and VELOCITY-136) just doesn't work.
#macro(NewMacro) do something #end
Here's why. Case 1. If "velocimacro.permissions.allow.inline.local.scope" is true, then #NewMacro is defined in "macros.txt" but not "file1.vm". Thus the macro is not available for use.
Case 2. If "velocimacro.permissions.allow.inline.local.scope" is false, then then #NewMacro is defined globally and available from all templates. This seems like a good idea but is impractical in practice for the following reasons.
- First of all, it doesn't work. Hence the first three bugs. They are technically not bugs since this behavior is
documented, though that's difficult to find.
- Currently macros are evaluated at parse time while #parse is evaluated at runtime. (allowing the template to dynamically choose a page to include). That's why the macro is not picked up by the #parse.
- Even if we fix this, there's a threading problem and a namespace problem. This macro is available to any template. If a website has hundreds or thousands of templates, there's no guarantee that another page hasn't defined a macro with the same name which could be loaded into the same common name space unexpectedly. The results could be very hard to debug.
Proposed Solution: Add a new mode for macros. (really, this should be the default in the future), which is request based. When a macro is defined in a page, it should be follow the execution of the page, be available in any subpages called with #parse and be available to a calling page that has called this with #parse. However, the macro shouldn't interfere with the global namespace or any non-related templates.
Open issues / discussion points -
- Can this be done while preserving Velocity's high-performance template caching?
- If we add a new scope for macros, is this backwards compatible? (assuming we allow old modes to continue to work).