Defining your own directives

You can define user-defined directives using the macro directive.

A macro is a template fragment associated with a variable. You can use that variable in your template as a user-defined directive to help with repetitive tasks. For example, the following code creates a macro variable that prints a big ``Hello Joe!'':

<#macro greet>
  <font size="+2">Hello Joe!</font>
</#macro>  

The macro directive does not print anything; it just creates the macro variable, in this case, a variable called greet. Everything between <#macro greet> and </#macro> (called macro definition body) will be executed when you use the variable as a user-defined directive. You use user-defined directives by writing @ instead of # in the RPL tag and using the variable name as the directive name. Also, the end tag for user-defined directives is mandatory. So you use greet like this:

<@greet></@greet>  

Since <anything></anything> is equivalent to <anything/>, you should use the shorter form:

<@greet/>  

Both examples produce this output:

  <font size="+2">Hello Joe!</font>

Note that since everything between <#macro ...> and </#macro> is a template fragment, it can contain interpolations (${...}) and RPL tags (e.g. <#if ...>...</#if>).

Specifying parameters

Let's improve the greet macro so it can use any name. For this purpose, you can use parameters. You define the parameters after the name of the macro in the macro directive. Macro parameters are local variables. For more information about local variables, see Working with variables.

The following example defines one parameter for the greet macro, person:

<#macro greet person>
  <font size="+2">Hello ${person}!</font>
</#macro>  

you can then use this macro as:

<@greet person="Fred"/> and <@greet person="Batman"/>  

which is similar to HTML syntax. The example produces this output:

 <font size="+2">Hello Fred!</font>
 and   <font size="+2">Hello Batman!</font>

The value of the macro parameter is accessible in the macro definition body as a variable (person). As with predefined directives, the value of a parameter (the right side of =) is an RPL expression. This means that the quotation marks around "Fred" and "Batman" are required. <@greet person=Fred/> means that you use the value of variable Fred for the person parameter, rather than the string "Fred". Parameter values need not be a string, they can be a number, a boolean, a hash, a sequence, ...etc. In addition, you can use complex expressions on the left side of = (e.g. someParam=(price + 50)*1.25).

User-defined directives can have multiple parameters. For example, to add a new parameter color:

<#macro greet person color>
  <font size="+2" color="${color}">Hello ${person}!</font>
</#macro>  

then you can use this macro as:

<@greet person="Fred" color="black"/>  

The order of parameters is not important, so the following example and the previous example are equivalent:

<@greet color="black" person="Fred"/>  

When calling the macro, you can use only those parameters defined in the macro directive and must specify a value for all parameters (in this case: person and color). The following example produces an error:

<@greet person="Fred" color="black" background="green"/> 

because the background parameter is not defined for the macro.

The following example also produces an error:

<@greet person="Fred"/> 

because the value of color is not specified.

If a parameter contains the same value most of the time, you can specify that value in the macro definition. To do this, define the parameter as: param_name=usual_value. This way, when using the macro you do not have to specify the parameter since its value is already provided. To use  a different value,  specify that value when using the directive. For example, to use "black" for color unless you specify a different value, define the parameter as:

<#macro greet person color="black">
  <font size="+2" color="${color}">Hello ${person}!</font>
</#macro>  

Now, you can use the macro as:

 <@greet person="Fred"/>

since it is equivalent to <@greet person="Fred" color="black"/> because the value of color parameter is known. If color is "red", use the macro as:

 <@greet person="Fred" color="red"/>

NOTE: Per RPL expression rules, the expressions someParam=foo and someParam="${foo}" are different. The first case uses the value of variable foo as the value of the parameter. The second case uses a string literal with an interpolation, so the value of the parameter will be a string -- in this case, the value of foo converted to text -- regardless of the variable type. As another example: someParam=3/4 and someParam="${3/4}" are different. The second case converts ¾ to a string, so if the directive expects a numerical value for someParam, an error will occur.

Nested content

User-defined directives can have nested content, similarly to predefined directives. To do this, use the nested directive. For example, the following code creates a macro that draws borders around its nested content:

<#macro border>
  <table border=4 cellspacing=0 cellpadding=4><tr><td>
    <#nested>
  </tr></td></table>
</#macro>  

<#nested> executes the template fragment between its start and end tags. So the following example:

<@border>The bordered text</@border>  

produces this output:

  <table border=4 cellspacing=0 cellpadding=4><tr><td>
    The bordered text
  </td></tr></table>

You can call the nested directive multiple times, for example:

<#macro do_thrice>
  <#nested>
  <#nested>
  <#nested>
</#macro>
<@do_thrice>
  Anything.
</@do_thrice>  

produces this output:

  Anything.
  Anything.
  Anything.  

Note that if you do not use the nested directive, the nested content will not be executed. In the following example, nested content will be ignored, since the greet macro does not use the nested directive:

<@greet person="Joe">
  Anything.
</@greet>  

produces this output:

<font size="+2">Hello Joe!</font>  

The nested content can be any valid RPL, including other user-defined directives. For example:

<@border>
  <ul>
  <@do_thrice>
    <li><@greet person="Joe"/>
  </@do_thrice>
  </ul>
</@border>  

produces this output:

  <table border=4 cellspacing=0 cellpadding=4><tr><td>
      <ul>
    <li><font size="+2">Hello Joe!</font>
 
    <li><font size="+2">Hello Joe!</font>
 
    <li><font size="+2">Hello Joe!</font>
 
  </ul>
 
  </tr></td></table>  

The local variables of a macro are not visible in the nested content. Because the y, x, and count are the local variables of the macro and are not visible outside of the macro definition, the following example:

<#macro repeat count>
  <#local y = "test">
  <#list 1..count as x>
    ${y} ${count}/${x}: <#nested>
  </#list>
</#macro>
<@repeat count=3>${y} ${x} ${count}</@repeat>   

Outputs an empty string since these variables are consided to be undefined:

    test 3/1:   
    test 3/2:   
    test 3/3:   

A different set of local variables is used for each macro call, so the following example is correct:

<#macro test foo>${foo} (<#nested>) ${foo}</#macro>
<@test foo="A"><@test foo="B"><@test foo="C"/></@test></@test>  

and produces this output:

A (B (C () C) B) A  

Using loop variables in macros

User-defined directives can have loop variables. For example, let's extend the do_thrice directive of the earlier examples so it exposes the current repetition number as a loop variable. The name of the loop variable is given when calling the directive, while the value of the variables is set by the directive itself. For example:

<#macro do_thrice>
  <#nested 1>
  <#nested 2>
  <#nested 3>
</#macro>
<@do_thrice ; x> <#-- user-defined directive uses ";" instead of "as" -->
  ${x} Anything.
</@do_thrice>  

produces this output:

  1 Anything.
  2 Anything.
  3 Anything.

You pass the value of the loop variable for a loop (i.e. repetition of nested content) as the parameter of nested directive. The name of the loop variable is specified in the user-defined directive open tag (<@...>) after the parameters and a semicolon.

A macro can use more the one loop variable. The order of variables is significant. In the following example, c, halfc, and last are loop variables:

<#macro repeat count>
  <#list 1..count as x>
    <#nested x, x/2, x==count>
  </#list>
</#macro>
<@repeat count=4 ; c, halfc, last>
  ${c}. ${halfc}<#if last> Last!</#if>
</@repeat>  

produces this output:

  1. 0.5
  2. 1
  3. 1.5
  4. 2 Last!

You can specify a different number of loop variables in the directive start tag (after the semicolon) than with the nested directive. If you specify less loop variables after the semicolon, the extra values in the nested directive will not be used, since there is no loop variable to hold those values. For example, the following code is correct:

<@repeat count=4 ; c, halfc, last>
  ${c}. ${halfc}<#if last> Last!</#if>
</@repeat>
<@repeat count=4 ; c, halfc>
  ${c}. ${halfc}
</@repeat>
<@repeat count=4>
  Just repeat it...
</@repeat>  

If you specify more variables after the semicolon than with the nested directive, the last few loop variables will not be created (i.e. will be undefined in the nested content).

For more information, see “User-defined directive call” and “Macro directive” sections.

NOTE: You can also define methods in RPL. For more information, see the function directive.You can use namespaces to organize and reuse commonly used macros. For more information about namespaces, see Namespaces.

Next steps

Working with variables

Most commonly used directives

Using directives together

Learn more

Directive Reference

Reserved Names in RPL