velocity-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Nathan Bubna <>
Subject Re: upgrading from 1.5 to 1.7 compatibility issues
Date Mon, 30 Apr 2012 16:55:26 GMT
Yeah, it was intended, and part of an overall move toward
fixing/simplifying Velocity's variable scoping, avoiding the
complexities and costs (performance, yes, but mostly time/brainpower
for users and devs alike) of more programming language type behavior.
Velocity has long aspired to be a straightfoward template engine and
avoid being a complete scripting language.  (Implicit) variable
scoping, as seen in 1.5, was seen as a necessary compromise toward the
latter; after all, one big fat namespace is always unmanageable,
right?  Well, there's ways to make that easy to manage. :)  Let's call
it "optional, provided, explicit scoping", explicit because you don't
have to grok the contextual scope to understand a reference, optional
because you can ignore it, and provided because Velocity does the work
of choosing "prefixes" and creating/destroying the scopes (as any
implicit scoping system does).  So everything is becoming globally
scoped, but it is now trivial to turn on automatic, explicit scopes or
namespaces that you can use when you don't want things to live in the
global scope.

Here's an example...  Do you use $velocityCount to get an index of
sorts inside of #foreach directives?  Well, that's an example of mixed
implicit/explicit namespacing that gets messy when you nest
#foreach's, with no good way to get the parent's count and
unwieldiness when you want to add $velocityIndex, $velocityHasNext and
so on.  Now, we automatically manage a $foreach var that not only has
a 'count' property, but an 'index', 'hasNext', 'parent', and so on
 It also, of course, accepts any property you want to set on it (like
any map).  This makes templates instantly understandable, making
debugging much better.  You always know exactly what you are referring
to, and so does anyone else reading the template.

#foreach is the only 'content directive' that has its explicit scope
automatically turned on, but all content containing directives
(including custom body macros) can have their own explicit,
auto-managed scope, named after themselves.  for example, you can flip
the macro scope on:

macro.provide.scope.control = true

and do:

#macro( outer $arg )
  #set( $macro.arg = $arg )
  #inner( 'inner' )
#macro( inner $arg )
  #set( $macro.arg = $arg)
  inner: $macro.arg
  #if( $macro.parent )outer: $macro.parent.arg#end

#outer( 'outer' )
#inner( 'just inner' )

and get

  inner: inner
  outer: outer
  inner: just inner

Hope this helps...

In any case, there was plenty of thought and discussion that went into
this change.  Search for 'scope' and you
should find more on this.

On Mon, Apr 30, 2012 at 8:49 AM, Boris Partensky
<> wrote:
> Hello, while going through the upgrade I noticed an incompatible
> behavior during nested macro evaluation. Looks like in 1.7 (all
> default properties) child macro has access to variables set in parent
> macro scope (and those take precedence over globals), and 1.5 sees
> globals. In the following example, in 1.5 unit test the following
> template will evaluate to "globalvar", and in 1.7 - to
> "outermacroparam". Is this expected behavior?
> 1.5 test case
> public void testVelocityNestedMacroScope() throws Exception
>    {
>        VelocityEngine ve = new VelocityEngine();
>        ve.init();
>        String template = "#macro(outerMacro $arg1)"+
>                          "#innerMacro('blah')"+
>                          "#end"+
>                          "#macro(innerMacro $arg2)$arg1#end"+
> "#set($arg1='globalval')#outerMacro('outermacroparam')";
>        StringWriter eval = new StringWriter();
>        boolean b = ve.evaluate(new VelocityContext(), eval, "foo", template);
>        assertEquals(eval.toString(), "globalval", eval.toString());
>    }
> 1.7 test case
>  public void testVelocityNestedMacroScope()
>    {
>        String template = "#macro(outerMacro $arg1)"+
>                          "#innerMacro('blah')"+
>                          "#end"+
>                          "#macro(innerMacro $arg2)$arg1#end"+
> "#set($arg1='globalvar')#outerMacro('outermacroparam')";
>        String eval = evaluate(template);
>        assertEquals(eval, "outermacroparam", eval);
>    }
> ---------------------------------------------------------------------
> To unsubscribe, e-mail:
> For additional commands, e-mail:

To unsubscribe, e-mail:
For additional commands, e-mail:

View raw message