Most frameworks today have pretty good support for code/markup reuse towards providing a common look to a site without the "include a header, include a footer" clunkers you used to see. (Spyce uses "parent tags," which are as elegant as any and more powerful than most.)
But what I want to talk about today is code/markup reuse in the small, at a level that corresponds to functions or methods in Python.
Most frameworks today still suck at this. Approaches vary, but the important thing they have in common is not letting you use the same techniques you use in the rest of the framework as well as generally being clunky.
For instance, with Rails, you define functions which you can then use from your views/templates/presentation layer, but within those functions you're back in 1996, stringing HTML together manually. Something like
def faq_html(question, answers) html = ''' <table class="faq"> <thead> <tr><th>Q.</th><th>%s</th></tr> </thead> ''' % question for a in answers: html += '<tr><td>A.</td><td>%s</td></tr>' % a html += '</table>' return html
(Edit: Rails also has a code reuse mechanism called "partials," which behave a lot more like normal rhtml code.)
In Spyce, you can still do things this old-fashioned way if you really want. But a much better way is defining "active tags," which take parameters like functions but are defined with the same tools you build the rest of your page with, i.e., Python and other tags. Such as:
[[.begin name=html singleton=True ]] [[.attr name=question ]] [[.attr name=answers ]] <spy:table class="faq" data="(('A.', a) for a in answers)"> <thead> <tr><th>Q.</th><th>[[= question ]]</th></tr> </thead> </spy:table> [[.end ]]
In a tag library named "faq", this active tag could then be used as follows: (the "=" sign means, "evaluate this parameter as a Python expression instead of as a string)
<faq:html question="=question.body" answers="=[a.body for a in answers]" />
The more complex your code becomes, the more you'll appreciate the Spyce way. You'll find that with the increased programmer-friendliness active tags provide, you'll abstract more common code than you would have with clumsier tools, with the all the improved maintainability that entails over code-by-copy-and-paste.
But what's really cool is you can encapsulate handlers with your tags, creating self-contained bundles of render and control logic. This is crucial: there's no way to do this with the traditional "helper function" approach. Splitting functionality out into active tag components like this allows both reuse and encapsulation, just as classes provide similar benefits in pure Python code.
(We talked about Spyce handlers yesterday. Briefly: Spyce allows you to attach a python function to a form submit action.)
To continue our FAQ example, let's add a "Create a new FAQ" handler, like this:
[[! def faq_new(self, api, question, answer): q = api.db.questions.insert(body=question) api.db.answers.insert(question_id=q.id, body=answer) api.db.flush() ]] [[.begin name=faq_html singleton=True ]] [[.attr name=question ]] [[.attr name=answers ]] <spy:table class="faq" data="(('A.', a) for a in answers)"> <thead> <tr><th>Q.</th><th>[[= question ]]</th></tr> </thead> </spy:table> <h2>New FAQ</h2> <div><f:text name=question label="Question:" /></div> <div><f:text name=answer label="Answer:" /></div> <div><f:submit handler=self.faq_new value="Create" /></div> [[.end ]]
Spyce provides friendlier and more powerful tools for code reuse than either other view-oriented frameworks or MVC frameworks. Give it a try; you won't want to go back.
Tomorrow I'll explain the Spyce authentication system.