Friday, August 16, 2013

Kajiki, Blocks, and Nesting

Last week I switched one of our newer projects to the Kajiki template engine from Genshi. Genshi's flawed mental model and py:match "misfeature" (as Kajiki's documentation rather gently puts it) had become too much of a roadblock to creating dynamic web applications built of small, composeable, reusable fragments.  While there are many worthy contenders in the Python template engine space, Kajiki's Genshi-like syntax made it somewhat less foreign and therefore a bit of an easier sell to others on the team.  Thus far I am pleased with the switch; Kajiki's py:import directive alone is nearly enough to justify changing.

The biggest difference between the two systems is their handling of inheritance: Kajiki jettisons Genshi's problematic include/match template model for true template inheritance with Jinja-inspired blocks.  As a quick example, a minimal parent template such as

<!DOCTYPE html>
<html>
<body>
    <div py:block="content">Eat your veggies!</div>
</body>
</html>

defines a block whose content can optionally be replaced by a child template:

<py:extends href="parent.html">            
    <py:block name="content">
        Candy and TV! (parent said: ${parent_block()})
    </py:block>                                 
</py:extends>

This simple example nevertheless illustrates the main features of Kajiki's inheritance scheme:
  • the parent template defines a block that provides an extension point that descendant templates can manipulate
  • the child template extends the parent template the same way a child class extends a parent class
  • the child template can override the parent's block by defining its own block with the same name; the special parent_block() function provides the child block access to the parent's content
One of the bumps we ran into related to nested blocks: is it possible to nest a block inside another block? In our application, the use case for this is navigation: our site uses two-level navigation, so a page will generally define the elements of the secondary navigation itself while inheriting its main navigation from the master template for that section of the site.

While struggling to fit the pieces together in the midst of a template system conversion, I came up with the following example to help me understand how Kajiki's block system handles nesting.  Here is the slightly modified parent template:

<!DOCTYPE html>
<html>
<body>
    <div py:block="content">
        Eat your veggies!
        
        <span py:block="nest">nested block</span>
    </div>
</body>
</html>

Notice we have the same content block but with another block nested inside of it.  Let's leave the child template exactly as before and create a third template extending the child:

<py:extends href="child.html">
    <py:block name="nest">
        Nested content (nested parent said: ${parent_block()})
    </py:block>
</py:extends>

Rendering this template produces this output from the content block:

Candy and TV! (parent said: <div>
    Eat your veggies!
 
    Nested content (nested parent said: <span>nested block</span>)
</div>)

Which is pretty much exactly how I would have expected this to work.

Download the full examples and play around with them to solidify the interaction between the three templates; both are self-contained Python scripts that depend only on Kajiki to work.  In particular, notice how the organizational hierarchy of the nested blocks is preserved, even after the child template wraps the parent block with additional content.

As a challenge, what do you think happens if nested inherits directly from parent (instead of child)?  How about if you remove the child's call to parent_block()?

1 comment:

  1. In particular, notice how the organizational hierarchy of the nested blocks is preserved, even after the child template wraps the parent block with additional content.speech recognition software

    ReplyDelete