METAL Specification Version 1.1¶
The Macro Expansion Template Attribute Language is an Attribute Languages for structured macro preprocessing. It can be used in conjunction with or independently of Template Attribute Language, Template Attribute Language Expression Syntax and ZPT.
Macros provide a way to define a chunk of presentation in one template, and share it in others, so that changes to the macro are immediately reflected in all of the places that share it. Additionally, macros are always fully expanded, even in a template’s source text, so that the template appears very similar to its final rendering.
The METAL namespace URI and recommended alias are currently defined as:
The following are the names of the required TAL 1.0 statements:
Each statement is described below, along with its argument syntax.
Although METAL does not define the syntax of ‘expression’ non-terminals, leaving that up to the implementation, a canonical expression syntax for use in METAL arguments is described in TALES Specification Version 1.0.
argument ::= Name
To define a macro, choose a name for the macro and add a ‘define-macro’ attribute to a document element with the name as the argument. The element’s subtree is the macro body. ‘define-macro’ may not be combined with ‘use-macro’. Example:
<p metal:define-macro="copyright"> Copyright 2001, <em>Foobar</em> Inc. </p>
argument ::= expression
To extend an existing macro, choose a name for the macro and add a ‘define-macro’ attribute to a document element with the name as the argument. Add an ‘extend-macro’ attribute to the document element with an expression referencing the base macro as the argument. The ‘extend-macro’ must be used in conjunction with ‘define-macro’, and must not be used with ‘use-macro’. The element’s subtree is the macro body. Example:
<div metal:define-macro="page-header" metal:extend-macro="standard_macros/macros/page-header"> <div metal:fill-slot="breadcrumbs"> You are here: <div metal:define-slot="breadcrumbs"/> </div> </div>
The rationale for the Extending Macros feature is given elsewhere.
argument ::= expression
To use a macro, first choose the document element that you want to replace. Add a ‘use-macro’ attribute to it, and set the value of the attribute to an expression that will return a macro definition. This will usually be some kind of path or template id and a macro name. It is important to remember that this expression will be evaluated separately from any [TAL] commands in the template, so it cannot depend on any such commands. ‘use-macro’ may not be combined with ‘define-macro’ or ‘extend-macro’.
PageTemplates use Template Attribute Language Expression Syntax for the expression, and you can use any of the standard context names in path expressions. Since they expose their collection of macro definitions through a ‘macros’ attribute, you can access individual macros easily.* For example:
<hr /> <p metal:use-macro="here/master_page/macros/copyright"> <hr />
The effect of expanding a macro is to graft a subtree from another document (or from elsewhere in the current document) in place of the statement element, replacing the existing subtree. Parts of the original subtree may remain, grafted onto the new subtree, if the macro has slots. If the macro body uses any macros, they are expanded first.
When a macro is expanded, its ‘define-macro’ attribute is replaced with the ‘use-macro’ attribute from the statement element. This makes the root of the expanded macro a valid ‘use-macro’ statement element.
define-slot and fill-slot¶
argument ::= Name
Macros are much more useful if you can override parts of them when you use them. For example, you might want to reuse a complex table, but provide different contents for one of the table cells in every place that you use it. It would be possible to place the contents somewhere on each Tempate, define a variable for it, and ‘insert’ that variable into the cell in the macro body. This, however, would violate the presentation structure, preventing you from seeing the table with the cell contents in place.
To make elements of the macro body overridable, add ‘define-slot’ attributes with the value set to a slot name. Wherever the macro is used, choose corresponding sub-elements of the statement element and add ‘fill-slot’ attributes with the value set to the slot name. When the macro is expanded, ‘fill-slot’ elements will replace the ‘define-slot’ elements in the macro body that use the same slot name.
Slot names must be unique within a single macro, and within a single macro use. It is legal, however, to define a slot in a macro and not.. fill it. This will simply cause the default contents of the slot definition to be copied into the expanded macro. If a ‘fill-slot’ element names a slot that is not found in the macro body, it causes an error.
In doc1: <table metal:define-macro="sidebar"> <tr><th>Links</th></tr> <tr><td metal:define-slot="links"> <a href="/">A Link</a> </td></tr> </table> In doc2: <table metal:use-macro="here/doc1/macros/sidebar"> <tr><th>Links</th></tr> <tr><td metal:fill-slot="links"> <a href="http://www.goodplace.com">Good Place</a><br> <a href="http://www.badplace.com">Bad Place</a><br> <a href="http://www.otherplace.com">Other Place</a> </td></tr> </table>
Notice that ‘doc2’, which uses the macro defined in ‘doc1’, contains the entire text of the ‘sidebar’ macro except for the ‘links’ slot. This is because the macro is inserted every time the source of ‘doc2’ is edited.
When the ‘extend-macro’ statement is being used, the set of ‘define-slot’ statements that apply is determined slightly differently. Slots defined by the base macro remain available for the template to fill, unless the derived macro fills them itself. Each slot can be filled at most once. The derived macro is allowed to offer a replacement slot that may be filled in lieu of a slot from the base macro. The template has no way to determine which macro is providing the slot. If a template attempts to fill a slot which is not available because a derived macro filled it already but did not offer a new slot of the same name, the slot-filler from the template will be ignored, just like any filler for an undefined slot.