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)