Tonight I added Tiles-like functionality to the Spyce trunk.
Now, this doesn't mean I cloned Tiles in Spyce. I took the usual Spyce approach of adding functionality found in the JSP world without adding all the complexity. (Actually, in this case it's actually more influenced by the OpenACS master and slave tags, but how many people would that mean anything to? :)
Specifically I added a way to create consistent site layout templates in the reverse-include style familiar to users of all modern web frameworks. (No, ASP.NET isn't modern by this definition. But ASP.NET 2.0 will be. They call it "Master pages," and googling for that is an excellent way to find a lot of people talking about how this is the best thing since sliced bread. This entry is long enough without me reiterating the reasons why this is a Good Thing.)
Instead of including header and footer in your content page, your content page declares that it belongs to a parent page, which defines all your common markup and a placeholder for the child. Simple example:
child
<spy:parent title="child title" />
Child content
parent
<html>
<head>
<title>[[= child['title'] ]]</title>
</head>
<body>[[= child['_body'] ]]</body>
</html>
which results in the final html of
<html>
<head>
<title>child title</title>
</head>
<body>Child content</body>
</html>
This is both recursive and dynamic.
Recursive, in that one parent template can itself be the child of another. This is useful wherever you have shared content or navigation on a group of pages; you can make them children of parent1 which is a child of your master parent, so site-wide changes still only need to be made in one place, and changes affecting only this group are also only made in one place.
Dynamic, in that all the arguments to spy:parent are evaluated at runtime, even the src argument. Here's an example that doesn't do much except demonstrate this:
[[ import os
cwd = os.getcwd()
s = '/parenttable.spy'
]]
<spy:parent src="=s" foo="=cwd" />
This results in the template located at /parenttable.spy being used instead of the default, and an argument named foo being passed with the result of the getcwd evaluation. (Changing which parent template to use at runtime is primarily useful for localizing different languages; passing other parameters that are evaluated at runtime is something you will use frequently even if you only care about English.)
In my opinion it's a phenomenal example of how robust Rimon's Spyce framework is that the diff for this changeset is only about 70 lines. Spyce is one of those rare projects that's a real pleasure to work on because the original author found just the right balance between thinking for future extensibility and overengineering. Tough line to walk, and Spyce does it well.