2012-09-20 140 views
0

我有一对夫妇的速度宏是这样的:封闭在Velocity模板的宏

#macro(Alpha) 
    #set($p = 1) 
    #@Beta() 
    $p    // 1 
    $bodyContent 
    #end 
#end 

#macro(Beta $params) 
    #set($p = 2) 
    $p    // 2 
    $bodyContent 
#end 

而我使用它们像这样:

#set($p = 0) 
#@Alpha(...) 
    $p    // 3 
#end 

我相信,这使得像这样(忽略格式化):2,2,2

但我想有适当的闭包行为,包括更多本地范围的名称隐藏父范围名称。具体来说,标记为'3'的$ p的使用应该指向值0,'2'指向值2,'1'指向值1.

给定适当的闭包语义,它将打印:2 ,1,0

有没有什么办法得到这个,或者一种实现自定义指令/修改#macro指令行为的方法来实现这个?

回答

5

Velocity是一个模板引擎,不是一种正确的编程语言,所以掌握它的工作原理有点困难。

宏不像Java或C中的函数,这意味着调用宏不会在堆栈上为局部变量创建新的段;速度与上下文一起工作,大部分时间只有一个全局上下文。

不过,也有处理局部变量的方法有两种:

  1. 有阻止改变从宏中的全局变量velocimacro.context.localscope配置参数;注意,此设置已被弃用,并将在速度2.0移除
  2. 可以使用$macro变量作为私有变量本地容器如果启用macro.provide.scope.control配置参数

不过,有会阻止另一个问题您的代码正确运行:Velocity macros work mostly as call-by-macro expansion,这意味着传递给宏的主体不会先评估,然后传递给宏;它将在嵌套宏执行时动态评估。该代码的行为如下:

#macro(Alpha) 
    #set($macro.p = 1) -> $macro refers to Alpha 
    $p -> $p will always be 0, since we're changing a local variable 
    $macro.p -> 1 here 
    $bodyContent -> Here $p is used, and 0 is printed 
    #@Beta() 
    $macro.p -> it is 1 now, but when this line will be actually evaluated, $macro will refer to Beta, so 2 will be printed 
    $bodyContent -> It's the content of the Beta macro, which triggers an infinite recursion; it's not $p as above 
    #end 
#end 

#macro(Beta) 
    #set($macro.p = 2) -> $macro refers to Beta 
    $macro.p -> 2 here 
    $bodyContent -> the $bodyContent that was passed into the $bodyContent is evaluated now, so it causes a recursion 
#end 

#set($p = 0) 
#@Alpha() 
    $p -> Global variable 
#end 
+0

我已阅读了关于这个神奇的$宏变量,但无法让它工作,所以非常感谢你的配置属性。关于按名称调用的问题,我也意识到了这一点,并且实际上做了一个定制构建,它热切地评估正文并将其作为纯文本节点传递给宏的$!bodyContent(或其配置为的任何变量)变量。 – gzak

+0

我还有一个跟进问题:除了$ macro之外,还有其他类型的局部变量,例如#foreach指令中的$ foreach。有没有#if?在文档中也有一个名为$ foo(可能作为示例),它对应于#@ foo()... #end指令中的局部变量。这些范围的各种配置属性名称(特别是#if和示例$ foo中的一个)是什么? – gzak

+0

所有配置属性都列在[documentation](http://velocity.apache.org/engine/releases/velocity-1.7/developer-guide.html#Velocity_Configuration_Keys_and_Values)中。 '#if'块没有范围变量。搜索'''somebodymacro> .provide.scope.control'来查看如何为名为'#foo'的宏配置像'$ foo'这样的范围变量。 –