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.
Comments
Piecing together an html page from HTML fragments is not what most templating languages do ... your argument that Spyce does it better thus is lacks convincing power ... I pity the fool who generates html by adding via +=
If you want to convice people that the Spyce way is better, compare it to Cheetah, Django or Kid.
just an opinion
If you've never felt the need to reuse a section of template -- or split it up into sections that "do one thing well" -- the way you reuse or split Python code into functions or classes, all I can say is, try it, you've been missing out. :)
But even if you are down to a helper function, ElementTree beats string concatenation.
Honestly, what I'd like is for some of those other frameworks' designers to see this and think, "Huh, that's a good idea. I'll figure out a way to make my stuff at least that elegant." Then everybody wins.
(Edit: ah, you're supposed to inherit from "utility templates" and use them that way. Okay, Kid gets a pass on that part. :)