5.4.1 <al-macro>/<al-usearg>/<al-expand>/<al-setarg>

The <al-macro> tag is used to define a macro.

Executing the macro registers the macro with the execution context via the register_macro() method using the name in the name attribute.

Note that the execution of the macro content is deferred until later when the macro is expanded via the <al-expand> tag. This means that executing a macro definition produces no output. Output is produced only when the macro is executed.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-macro name="noargs">
...  Will be executed when macro is expanded.
... </al-macro>
... ''').to_html(ctx)
>>> ctx.flush_content()
>>> albatross.Template(ctx, '<magic>', '''
... <al-expand name="noargs"/>
... ''').to_html(ctx)
>>> ctx.flush_content()
Will be executed when macro is expanded.

The deferred execution also means that you can include content that only works within the context of the <al-expand> tag.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-macro name="oops">
...  <al-value expr="oops">
... </al-macro>
... ''').to_html(ctx)
>>> ctx.flush_content()
>>> templ = albatross.Template(ctx, '<magic>', '''
... <al-expand name="oops"/>
... ''')
>>> try:
...     templ.to_html(ctx)
...     ctx.flush_content()
... except NameError, e:
...     print e
... 
name 'oops' is not defined
>>> ctx.locals.oops = 'there is now'
>>> templ.to_html(ctx)
>>> ctx.flush_content()
there is now

In the above example the content of the macro makes reference to a oops that is not defined in the execution context when the macro was defined.

The <al-usearg> tag is used in the macro definition to define where content enclosed by the <al-expand> tag should be placed when the macro is expanded. All content enclosed by the <al-expand> tag is passed to the macro as the unnamed argument. The unnamed argument is retrieved in the macro definition by using an <al-usearg> tag without specifying a name attribute. When a macro expects only one argument it is best to use this mechanism.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-macro name="double">
...  1. <al-usearg>
...  2. <al-usearg>
... </al-macro>
... ''').to_html(ctx)
>>> ctx.flush_content()
>>> albatross.Template(ctx, '<magic>', '''
... <al-expand name="double">
...  spam
... </al-expand>''').to_html(ctx)
>>> ctx.flush_content()
1. spam
2. spam

Inside a macro definition you can use as yet undefined macros.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-macro name="bold-red">
... <font color="red">more <al-expand name="bold"><al-usearg></al-expand> please</font>
... <font color="red"><al-expand name="bold">more <al-usearg> please</al-expand></font>
... </al-macro>
... 
... <al-macro name="bold">
...  <b><al-usearg></b></al-macro>
... ''').to_html(ctx)
>>> ctx.flush_content()
>>> albatross.Template(ctx, '<magic>', '''
... <al-expand name="bold-red">spam</al-expand>
... ''').to_html(ctx)
>>> ctx.flush_content()
<font color="red">more <b>spam</b> please</font>
<font color="red"><b>more spam please</b></font>

Care must by taken to ensure that you do not make circular macro references else you will cause a stack overflow.

You can also pass macro expansions as arguments to other macros.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-macro name="red">
... <font color="red"><al-usearg></font>
... </al-macro>
... 
... <al-macro name="bold"><b><al-usearg></b></al-macro>
... ''').to_html(ctx)
>>> ctx.flush_content()
>>> albatross.Template(ctx, '<magic>', '''
... <al-expand name="red">more <al-expand name="bold">spam</al-expand> please</al-expand>
... <al-expand name="red"><al-expand name="bold">more spam please</al-expand></al-expand>
... ''').to_html(ctx)
>>> ctx.flush_content()
<font color="red">more <b>spam</b> please</font>
<font color="red"><b>more spam please</b></font>

All arguments to macros are executed before they are passed to the macro. This means that there are no side effects for using arguments more than once inside a macro.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-macro name="yummy">
...  <al-for iter="i" expr="range(3)">
...   <al-usearg whitespace="indent">
...  </al-for>
... </al-macro>
... ''').to_html(ctx)
>>> ctx.locals.food = 'spam'
>>> albatross.Template(ctx, '<magic>', '''
... <al-expand name="yummy">
...  <al-exec expr="food = food + '!'">
...  <al-value expr="food">
... </al-expand whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
spam! spam!! spam!!! 

Macros can be defined to accept multiple arguments. The name attribute of the <al-usearg> tag is used to retrieve named arguments. When invoking a macro that accepts named arguments the <al-setarg> tag and name attribute is used to define the content for each named argument.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-macro name="namedargs" whitespace="indent">
...  The unnamed arg (<al-usearg>) still works,
...  but we can also include named arguments (<al-usearg name="arg1">)
... </al-macro>
... ''').to_html(ctx)
>>> albatross.Template(ctx, '<magic>', '''
... <al-expand name="namedargs">
...  unnamed arg<al-setarg name="arg1">
...  named arg: arg1</al-setarg>
... </al-expand>
... ''').to_html(ctx)
>>> ctx.flush_content()
 The unnamed arg (unnamed arg) still works,
 but we can also include named arguments (named arg: arg1)