Overview of makiLogic Processor
makiLogic modifies a document by searching through it for various elements in the maki namespace ('http://maki.sourceforge.net/maki') and replacing them with dynamic text, elements, or attributes. You can invoke the processor with a step like this:
<process module='spb.makiLogic' function='process' />
|
|
or simply rely on the default values for @module and @function:
I will now discuss all of the elements that makiLogic knows how to handle.
The <maki:logic> Element
<maki:logic> contains text that is evaluated as Python code. Be aware that as with all Python source code, whitespace and indentation are important. Also be sure to encode any special XML characters that appear in the code text ('<' as '<' and '>' as '>' and '&' as '&') or enclose the text in a CDATA section. After the code is processed, the <maki:logic> tag is removed from the document.
The <maki:expr> Element
<maki:expr> contains text that is evaluated as a Python expression. In general the result of that expression is then coerced to text (using str()) and that text is used to create a text node that replaces the <maki:expr> tag. This is what is done for "simple" values (strings, numbers, and most objects), but there are some special cases.
If the expression evaluates to an object that has a method named toSaxEvents() then that method is called and the makiLogic handler is passed to it. This allows you to have complete control over how your classes are serialized to XML by maki. If you want to add a toSaxEvents() method to any of your classes, it should take one argument: a SAX handler object which you can call methods on (such as startElementNS() or startElement(), endElementNS() or endElement(), and characters().
If the expression evaluates to a list or tuple, then the list is iterated through and several elements named "item" are created. Each <item> contains the value of that list item (either coerced to text if a simple value or recursively handled otherwise).
For example:
<maki:logic> a_list = [] a_list.append("this") a_list.append("is") a_list.append("a") a_list.append("test") </maki:logic> <maki:expr>a_list</maki:expr>
|
|
will be replaced by:
<item>this</item> <item>is</item> <item>a</item> <item>test</item>
|
|
If the expression evaluates to a dictionary, then there are two possible ways that the dictionary might be serialized to XML, depending on the value of the <maki:expr> attribute @keyStyle. If @keyStyle='element', then
the keys of the dictionary are iterated through and several elements are created. Each element has the name of a key value and contains the value of that dictionary item (either coerced to text if a simple value or recursively handled otherwise).
If @keyStyle='attribute', then one element named "item" is created for each key, and the value of the key is set in an attribute named "key". This is the default behaviour if you don't specify @keyStyle.
For example, assuming you have a dictionary like this one:
<maki:logic> a_dict = {} a_dict["color"] = "blue" a_dict["num"] = 909 a_dict["name"] = "Sam" </maki:logic>
|
|
Then the following
<maki:expr keyStyle="element">a_dict</maki:expr>
|
|
will be replaced by:
<num>909</num> <name>Sam</name> <color>blue</color>
|
|
And this
<maki:expr keyStyle="attribute">a_dict</maki:expr>
|
|
or this
<maki:expr>a_dict</maki:expr>
|
|
will be replaced by:
<item key="num">909</item> <item key="name">Sam</item> <item key="color">blue</item>
|
|
Since the order of dictionary keys is undefined, the order of the generated elements is also undefined.
Finally, <maki:expr> can have an optional attribute @name. If present, maki will wrap the expression output inside an element with that name.
For example:
<maki:expr>5 + 7</maki:expr>
|
|
will be replaced by:
But if there was a @name:
<maki:expr name="test">5 + 7</maki:expr>
|
|
the output would be:
The <maki:attribute> Element
<maki:attribute> adds an attribute to the parent element.
The name of the attribute is read from @name.
An optional attribute @uri allows you to specify the namespace URI. The value of the new attribute is produced by evaluating the text content of <maki:attribute> as a Python expression and coercing to text. After the code is processed, the <maki:attribute> tag is removed from the document.
Note that <maki:attribute> elements must be the first children of their parent element. If any other elements or character data comes first, the behaviour is undefined.
The <maki:element> Element
<maki:element> dynamically creates an element.
The name of the element is read from @name.
An optional attribute @uri allows you to specify the namespace URI.
The children of <maki:element> are then handled.
Character data is simply passed through, as are non-maki elements. Any maki elements are handled as usual.
For example:
<maki:element name="test">5 + 7</maki:element>
|
|
the output would be:
Note that you can dynamically name elements if the @name contains the special characters "{" and "}". See the section below on Handling of Attribute Values.
The <maki:elements> Element
<maki:elements> is a convenience for creating many elements from a Python list (or tuple). Its only advantage over using <maki:expr> is that it allows you to set the name of the elements (whereas <maki:expr> will always use the name "item").
Each element is named based on the value of @name. An optional attribute @uri allows you to specify the namespace URI. Each list item is handled like <maki:expr>.
Example:
<maki:elements name="foo">a_list</maki:elements>
|
|
will be replaced by:
<foo>this</foo> <foo>is</foo> <foo>a</foo> <foo>test</foo>
|
|
<maki:elements> also allows you to set @keyStyle (like <maki:expr>). This is useful in the case where you have a list of dictionaries and want to control how each dictionary is serialized.
The <maki:comment> Element
<maki:comment> dynamically creates a comment. The text of the comment is produced by evaluating the text content of <maki:comment> as a Python expression and coercing to text. The resulting comment replaces the <maki:comment> tag.
The <maki:processing-instruction> Element
<maki:processing-instruction> dynamically creates a processing instruction. The name (sometimes called the "target") of the processing instruction is read from @name. The data of the processing instruction is produced by evaluating the text content of <maki:processing-instruction> as a Python expression and coercing to text. The resulting processing instruction replaces the <maki:processing-instruction> tag.
The <maki:page> Element
<maki:page> is useful in situations where you would otherwise have more than one document element in your XML page. After processing by makiLogic, the <maki:page> layer is removed from the document.
As an example where this can be useful, consider the need to add a dynamic processing instruction to the top of your document (before the document element).
In order to do this, you would need to have a <maki:processing-instruction> before the document element. However, your pre-processed document would then be illegal, since it would have two top-level elements. To avoid that situation, you could wrap the two elements in a <maki:page>.
After processing, it would be stripped, and your resulting document would have a processing instruction and a single top-level (or "document") element.
Handling of attribute values
Most of the attributes that you set for elements that makiLogic handles will probably be static strings. However, you may find the need to use dynamic attribute values.
Whenever makiLogic reads the value of an attribute, it searches the string for substrings enclosed in { and } characters. Any such substrings are treated as Python expressions and are evaluated in place.
Of course, sometimes you might want a literal { or } in the attribute value. In such a case, use double curly braces (such as val="literal {{braces}}"). Note that this is the same convention used by XSLT to escape curly braces in attribute values.
The <maki:include> Element
While it is not a tag recognized by makiLogic and is instead handled directly by the maki engine, I will discuss <maki:include> here.
<maki:include> must have a @href whose value is a path to an XML file to be included. There is an optional @keepRoot which defaults to 'yes'. If 'yes', then the entire XML document is included. If 'no', then the root (or top-level) element is discarded, and only the children of the root are included. Sometimes this is just what you want, because you want to include more than one top-level element, but a valid document must only have one top-level element.
|