To give a feel for how Brevé looks compared to other template engines, I've converted the Pylons QuickWiki tutorial to Brevé and provided the original Myghty templates for comparison.
You may wonder why the Brevé template isn't always shorter. This is mostly due to Brevé's explicit inheritance style which adds around 4 lines per template. While not shown here, this explicit style allows for a flexible inheritance mechanism. Regardless of actual line count, the Brevé templates are typically much typographically cleaner and easier to read than the equivalent Myghty template. This particular example may have been a bit unfortunate since it fails to showcase some of Brevé's more interesting features, but I'm sure the same could be said of Myghty, so for the sake of simplicity I'm willing to live with that.
It should also be noted that I made a couple cosmetic changes to the Myghty templates, splitting tags over lines for both the sake of readability and to closer match my own style in Brevé. This seemed most fair to both (otherwise the Myghty templates appeared less readable and the Brevé templates appeared to have higher line counts). The goal is to compare template engines, not a particular programmer's style. The original templates are otherwise unaltered from the tutorial.
Note that web helpers that output XML must be surrounded by xml ( ) as this prevents their output from being quoted.
Finally, the original tutorial builds some of the templates in multiple steps. For the sake of brevity I've already completed all the steps and provide the finished examples.
Myghty (templates/autohandler):
<html>
<head>
<title>QuickWiki</title>
<% h.stylesheet_link_tag('/quick.css') %>
<% h.javascript_include_tag('/javascripts/effects.js', builtins=True) %>
</head>
<body>
<div class="content">
% m.call_next()
<p class="footer">
Return to the
<% h.link_to('FrontPage', h.url_for(action="index", title="FrontPage")) %>
% if h.url_for() != '/page/list':
| <% h.link_to('Edit '+c.title, h.url_for(title=c.title, action='edit')) %>
| <% h.link_to('Title List', h.url_for(action='list', title=None)) %>
% # end if
</p>
</div>
</body>
</html>
Brevé (templates/index.b):
html [
head [
title [ 'QuickWiki' ],
h.javascript_include_tag('/javascripts/effects.js', builtins=True)
],
body [
div ( class_="content" ) [
slot ( 'content' ),
p ( class_="footer" ) [
'Return to the ',
h.link_to ( 'FrontPage', h.url_for ( action="index", title="FrontPage" ) )
'|', h.link_to ( 'Edit ' + c.title, h.url_for ( title=c.title, action='edit' ) ),
'|', a ( render = c.render.list_link, data = h.url_for ( ) )
]
]
]
]
Note the use of the custom renderer in the footer. You can see how this is setup in controllers/page.py which is listed at the end of this document.
Myghty (templates/page.myt):
<h1 class="main"><% c.title %></h1>
% if c.message:
<p><div id="message"><% c.message %></div></p>
% #end if
<% c.content %>
Brevé (templates/page.b):
inherits ( 'index' ) [
override ( 'content' ) [
h1 ( class_ = 'main' ) [ c.title ],
p ( render = c.render.message, data = c.message ),
xml ( c.content )
]
]
We're also using a custom renderer in this template. See the controller at the end of this document for more information.
Myghty (templates/new_page.myt):
<h1 class="main"><% c.title %></h1>
<p>
This page doesn't exist yet.
<a href="<% h.url_for(action='edit', title=c.title) %>">
Create the page
</a>.
</p>
Brevé (templates/new_page.b):
inherits ( 'index' ) [
override ( 'content' ) [
h1 ( class_="main" ) [ c.title ],
p [
'This page doesn't exist yet.',
a ( href=h.url_for ( action='edit', title=c.title ) [
'Create the page.'
]
]
]
]
Myghty (templates/edit.myt):
<h1 class="main">Editing <% c.title %></h1>
<% h.start_form(h.url_for(action='save', title=c.title), method="get") %>
<% h.text_area(name='content', rows=7, cols=40, content=c.content)%> <br />
<% h.submit(value="Save changes", name='commit') %>
<% h.end_form() %>
Brevé (templates/edit.b):
inherits ( 'index' ) [
override ( 'content' ) [
h1 ( class_="main" ) [ 'Editing ' + c.title ],
form ( action = h.url_for ( action='save', title=c.title ), method="get" ) [
textarea ( name='content', rows='7', cols='40' ) [ c.content ],
input ( type='submit', value='Save changes', name='commit' )
]
]
]
Myghty (templates/titles.myt):
<h1 class="main">Title List</h1>
<div id="trash">
Delete a page by dragging its title here
</div>
<% h.drop_receiving_element("trash", update="titles", url=h.url_for(action="delete")) %>
<ul id="titles">
<% render('/list.myt', fragment=True) %>
</ul>
Brevé (templates/titles.b):
inherits ( 'index' ) [
override ( 'content' ) [
h1 ( class_='main' ) [ 'Title List' ],
div ( id="trash" ) [
'Delete a page by dragging its title here'
],
xml (
h.drop_receiving_element ( "trash", update="titles", url=h.url_for ( action="delete" ) )
),
include ( 'list' )
]
]
Myghty (templates/list.myt):
% for title in c.titles:
<li>
<span id="page-<% title %>"><% title %></span>
[<% h.link_to('visit', h.url_for(title=title, action="index")) %>]
<% h.draggable_element("page-"+ str(title), revert=True) %>
</li>
% #end for
Brevé (templates/list.b):
ul ( id="titles" ) [
[ li [
span ( id='page-%s' % t ) [ t ],
'[', h.link_to ( 'visit', h.url_for ( title=title, action='index' ) ), ']',
h.draggable_element ( 'page-%s' % t, revert=True )
] for t in c.titles ]
]
Note that a direct mapping of this template wasn't possible since Brevé templates must be complete sections. That is, there must be a single enclosing tag at the topmost level. The Myghty template could have been easily altered to have the <ul> tag in the fragment but I think it's an important point to note, so I left the distinction visible.
This is more or less the same as the one for Myghty, but there are a couple changes and I needed to show the custom renderers, so it's included here for completeness.
Brevé controller (controllers/page.py):
from quickwiki.lib.base import *
from quickwiki.lib.database import session_context
from breve.tags import html as H
class Class: pass
class PageController ( BaseController ):
def __before__ ( self ):
c.render = Class ( )
c.render.message = self.render_message
c.render.list_link = self.render_list_link
self.session = session_context.current
self.query = self.session.query ( model.Page )
def index ( self, title ):
page = self.query.get_by ( title=title )
if page:
c.content = page.get_wiki_content ( )
return render_response ( 'page' )
elif model.wikiwords.match ( title ):
return render_response ( 'new_page' )
abort ( 404 )
def edit ( self, title ):
page = self.query.get_by ( title=title )
if page:
c.content = page.content
return render_response ( 'edit' )
def save ( self, title ):
page = self.query.get_by ( title=title )
if not page:
page = model.Page ( )
self.session.save ( page )
page.title = title
page.content = request.params [ 'content' ]
c.title = page.title
c.content = page.get_wiki_content ( )
c.message = 'Successfully saved'
self.session.flush ( )
return render_response ( 'page' )
def list ( self ):
c.titles = [ page.title for page in self.query.select ( ) ]
return render_response ( 'titles' )
def delete ( self ):
title = request.params [ 'id' ][ 5: ]
page = self.query.get_by ( title=title )
self.session.delete ( page )
self.session.flush ( )
c.titles = self.query.select ( )
return render_response ( 'list', fragment=True )
# a couple custom renderers
def render_message ( self, tag, data ):
if data:
return tag [
H.div ( id="message" ) [ data ]
]
return ''
def render_list_link ( self, tag, data ):
if data == '/page/list':
return ''
return tag [ h.link_to ( 'Title List', data ) ]
I've provided an archive of the Brevé templates and controller to make it easier to play with. It isn't the entire project, just the notable differences.