Index ===== 1. What is Breve? a) A simple example b) Breve compared to other template engines 2. Basic HTML generation 3. Variable substitution 4. Special directives a) include b) inheritance c) conditionals 5. Custom flatteners 6. Custom renderers 7. Using Python features in templates a) statements vs expressions b) concrete examples 8. Putting it together A. TBD: Gotchas and tricks B. TBD: Extending Breve Tags C. TBD: Extending Breve Conditionals D. TBD: Creating new XML generators CHAPTER 1. What is Breve? ========================= Breve is a template engine inspired heavily by Nevow Stan [1]. Unlike most Python template engines, Breve is not an XML parser, rather Breve templates are expressed as genuine Python code with a single limitation: only expressions are allowed. A simple example ---------------- html [ head [ title [ 'A simple example' ] ], body [ h1 [ 'This is an example of Breve' ], br, div ( style = 'text-align: center;' ) [ span [ ''' As you can see, Breve markup maps very directly to the final HTML output. ''' ] ] ] ] This template would output the following: A simple example

This is an example of Breve

As you can see, Breve markup maps very directly to the final HTML output.
You can quickly see how Breve's syntax is far more terse than the generated HTML output. This is the first obvious advantage of Breve. Templates require less typing and appear less cluttered. Breve compared to other template engines ---------------------------------------- Most Python template engines (with only a few notable exceptions) are XML derivatives. That is, they are either valid XML (e.g. Zope's ZPT) or they are non-validating XML derivatives (i.e. Kid and Genshi) with template directives embedded within the XML. These types of template engines have the apparent advantage of being familiar to designers who may not be familiar with programming languages but know XML/HTML. While most designers may be familiar with X/HTML, they probably aren't familiar with the odd tags and directives these template engines insert into the template, so this perceived advantage is unsubstantiated. My experience has shown most template engines to be equally (in)comprehensible to the average designer. One notable advantage to Breve is that it consists of a single syntax. That is, there is not a separate syntax for markup and a separate syntax for template directives. There is only one syntax that is universal to the entire template. Another advantage to Breve is that because it is real Python code, rendering is fast. Most XML-based template engines require something like the following process to render: parse XML template -> generate DOM -> flatten DOM to HTML. Breve requires only: parse Python code -> flatten DOM to HTML This is because Breve templates *are* the DOM. Further, because Breve's parser is Python's parser, Breve takes advantage of any optimizations available in the Python parser. Also, once a Breve template has been parsed, the output (Python byte code) can be cached and reused for further performance gains. Another advantage Breve has over many other template engines is that it can be easily extended to generate other types of output besides HTML. For instance, Breve has been used to generate XRC, the XML micro-language used by wxWidgets [2] for describing graphical user interface elements. It's also been used to generate RSS and Atom feeds. Because Breve has a pluggable XML generation system (and defining new tags is remarkably simple) it's ideally suited for many applications outside the web. CHAPTER 2. Basic HTML Generation ================================= Breve supports all the expected HTML tags. Translating a typical HTML document is usually no more complicated than the following: 1) replace open tags with "tag [" 2) replace the close tags with "]" 3) put parentheses around attributes ( such as style="" and id="" ) 4) put commas between adjacent tags Consider the example given earlier to get a good picture of what this entails. Using Variables --------------- Template engines aren't of much use if you can't dynamically generate content (although Breve's clean syntax would be an advantage in any case). Using variables in Breve is quite simple: you simply pass the variable (usually from your framework's controller) and then use it in your template. An example for TurboGears [3] might be as follows: # controllers.py from datetime import datetime @view ( 'breve:.index.b' ) def index ( self, *args, **kw ): today = datetime.today ( ).strftime ( '%Y/%m/%d' ) return dict ( message = 'Hello, world', today = today ) # index.b html [ body [ span [ message ], br, span [ 'Today is ', today ] ] ] Note that Breve templates usually end with a ".b" extension (although this can be changed). The output of the above would be something like this: Hello, world
Today is 2006/12/27 CHAPTER 3. Special Directives ============================= Variable substitution is useful, but Breve supports much more powerful features that help organize your templates and assist with code reuse. The include directive --------------------- The first and simplest of these is simple file includes. Templates may include fragments of Breve from other files. For example: # index.b html [ body [ span ( class_ = 'title' ) [ 'Include directive' ], div ( class_ = 'para' ) [ include ( 'fragment' ) ] ] ] # fragment.b div [ 'This could have been any amount of Breve' ] You could also combine this with variable substitution to dynamically select fragments to include in your template, for example: # index.b html [ body [ span [ "We're going to include %.b" % page ], include ( page ) ] ] Assuming that the variable "page" was populated with the path to a file containing Breve code, this would have included whatever file was stored in that variable. However, there are better ways of doing this that we'll be covering next. Template Inheritance -------------------- Inheritance is more complicated than simple includes, but more powerful as well. At some level it can be seen as the inverse of includes, since it's the fragments that specify what they'll be included in. This model is especially useful in frameworks (such as TurboGears and CherryPy) where a class or method (referred to a a "controller") directly represent a particular page on a website. You *could* solve this problem by dynamically selecting files to include as in the earlier section, but it's much better to use inheritance. It's much easier to demonstrate than explain so here's a short example: # controllers.py @view ( 'breve:contact.b' ) def contact ( self, *args, **kw ): return ( { } ) # contact.b inherits ( 'index' ) [ override ( 'content' ) [ div ( id = 'contacts' ) [ ul [ li [ a ( href = 'mailto:cliff@domain.com' ) [ 'Cliff' ] ], li [ a ( href = 'mailto:laura@domain.com' ) [ 'Laura' ] ] ] ] ] ] # index.b html [ body [ slot ( 'content' ) ] ] This may seem complicated (and there are a few new directive that must be taken together), but it's really quite simple (especially if you are familiar with the concept of inheritance from Python and other programming languages). Here's the basic flow of events: The controller specifies that it will render the template "contact.b". However this template gives notice that it is only part of a larger whole by using the "inherits" directive. The "inherits" directive tells Breve that this template consists only of fragments that will fill in empty "slots" in some other template (whose name is given in the parentheses following the "inherits" directive). Now that Breve knows what *template* to fill in with fragments from this template, it needs to know what slots to fill in (our example has only one, but there could be several). This is what the "override" directive does. It tells Breve to replace the "slot" named "content" with the fragment specified. More than one slot may be given: # fragment.b inherits ( 'index' ) [ override ( 'main-menu' ) [ div ( class_ = 'menu' ) [ ul [ li [ a ( href = '/' ) [ 'Home' ] ], li [ a ( href = '/about' ) [ 'About' ] ] ] ] ], override ( 'content' ) [ div ( class_ = 'body-text' ) [ '''Welcome to our humble site. Enjoy your stay.''' ] ] ] # index.b html [ body [ slot ( 'main-menu' ), slot ( 'content' ) ] ] The output of the above would look like this:
Welcome to our humble site. Enjoy your stay.
Inheritance can go many layers deep and the deepest layer (i.e. the one farthest from the root template) has the final say. For example: # frag2.b inherits ( 'frag1' ) [ override ( 'slot-1' ) [ span [ 'Hello from frag2' ] ], override ( 'slot-2' ) [ span [ 'Hello from frag2' ] ] ] # frag1.b inherits ( 'index' ) [ override ( 'slot-1' ) [ span [ 'Hello from frag1' ] ], override ( 'slot-3' ) [ span [ 'Hello from frag1' ] ] ] # index.b html [ body [ slot ( 'slot-1' ), slot ( 'slot-2' ), slot ( 'slot-3' ) ] ] Then if we told our controller to render frag2.b, we would get the following: Hello from frag2 Hello from frag2 Hello from frag1 Notice that even though frag1.b specified that it would override slot-1, because frag2.b also specified that it would override slot-1 and, being further from the "root" document (in this case index.b), it has the last say and so does the override. As an aside, something that's been used several times now but not mentioned is appending an underscore ( _ ) to certain attributes. This is side-effect of using Python as a template language. Some words that are used as HTML attributes are also reserved Python keywords ("class" being the most commonly used one). Because it would be a Python syntax error to use the word "class" outside an actual class definition, we are forced to append an underscore to prevent this name clash. Helpfully, we don't need to actually remember which attributes conflict with Python keywords. If you don't know which might cause problems, simply append an underscore to *all* attributes. Breve will accept it either way. Conditional Expressions ----------------------- For the most part, it's best to not put too much logic in your templates. It tends to make them cluttered and difficult to read. Also, debugging templates is usually more difficult than debugging actual program code so the simpler they are the better. Regardless, it's sometimes useful to be able to embed bits of simple logic in templates. To that end, Breve provides a few simple constructs for making rudimentary decisions directly within the template. If you find yourself tempted to use these constructs, pause and consider if perhaps this could be better done within the controller. Usually the controller is the proper place for logic but on rare occassions it makes sense to do it in the template. Where to draw the line is something that comes with experience. If you find your templates getting difficult to understand then you've probably gone too much the wrong direction. The switch/case conditional --------------------------- The first conditional expression is the switch/case conditional. If you are familiar with languages such as C, then this is a familiar construct (albeit with somewhat different syntax). An example: html [ body [ switch ( username ) [ case ( None ) [ span [ "Please login" ] ], default [ "Welcome back, %s" % username ] ] ] ] In the above example, the switch/case conditional is used to test the value of the "username" variable. If the variable contains None (which we assume means no one is logged in), then we display a message directing the user to login. If it's *anything else*, then we assume it contains a valid username and we display a welcome message. There are a few things to note about this construct: 1) There can be as many "case" directives as you need 2) "case" directives are evaluated from top to bottom. This means you should probably put the most common cases near the top for performance reasons (although if your conditional is long enough to make much difference then probably you should have put this logic in the controller). 3) The "default" directive is optional and, if present, *must be last*. Evaluation of the conditional stops whenever a "case" directive meets the criteria or the "default" directive is encountered, so any further "case" directives after the default will never be evaluated. 4) If no "case" directive satisfies the test and there is no "default" directive, the expression as a whole evaluates to nothing (i.e. it won't appear in the final HTML output). CHAPTER 5. Custom Flatteners ============================ So far, most of the features of Breve are pretty typical of most template engines. In fact, Breve offers far fewer features than most (we consider this a good thing). However, Breve tries to offer the fewest but most powerful and expressive features. Quality over quantity. One of the simplest yet most powerful features of Breve is the concept of flatteners. As I mentioned earlier, there's only one process to turn a Breve template into its final HTML form and that process is called "flattening". All the keywords such as "div" and "span" that you use in Breve templates have a default flattener. In fact, anything that can be printed (i.e. using the "print" statement) in Python can be flattened automatically by Breve. However, sometimes we want certain things to be flattened in a particular way. Quite often the string representation of Python objects isn't at all what we'd want in our HTML output. Consider the first example back in Chapter 1 that used the datetime object. In that example, we formatted the output in the controller prior to passing it to the template. Let's try it again using a custom flattener: # controllers.py from datetime import datetime from breve.flatten import register_flattener def flatten_date ( o ): return o.strftime ( '%Y/%m/%d' ) register_flattener ( datetime, flatten_date ) @view ( 'breve:index' ) def index ( self ): today = datetime.today ( ) return dict ( today = today ) # index.b html [ body [ span [ 'Today is', today ] ] ] The function "register_flattener" takes two arguments: a type and a function. Whenever Breve encounters an object that matches the registered type, it will pass that object to the specified function to be flattened. The output of the function is what will appear in the final HTML output: Today is 2006/12/27 This is an extremely simple example, but it doesn't take much to imagine the ways this concept can be applied. Database records could be flattened to forms, Python lists could be flattened to HMTL tables or lists. The beautiful thing is that no special formatting need appear anywhere but in the flattener. Again this fits with the Breve philosophy of keeping templates as simple and clean as possible. CHAPTER 6: Custom Renderers =========================== Much as flatteners allow you to generate custom HTML when a particular Python object is encountered, renderers allow you to generate custom template code for particular template tags. While flatteners generate the final HTML, renderers generate template fragments. For example: # controllers.py @view ( template = 'breve:index' ) def index ( self ): def hello ( tag, data = None ): return tag [ span ( style = 'font-weight: bold;' ) [ 'Hello, world' ] ] return ( dict ( hello_renderer = hello ) ) # index.b html [ body [ div ( render = hello_renderer ) ] ] This would output:
Hello, world
You'll notice, however, that the renderer can accept a second argument "data". You can pass any value or variable in here, for example: # controllers.py @view ( template = 'breve:index' ) def index ( self ): def hello ( tag, data = None ): return tag [ span ( style = 'font-weight: bold;' ) [ 'Hello, %s' % data ] ] return ( dict ( hello_renderer = hello ) ) # index.b html [ body [ div ( render = hello_renderer, data = 'Joe' ) ] ] This would output:
Hello, Joe
Again, this is a very simple (and rather contrived) example, but these simple building blocks are quite powerful when combined in creative ways. CHAPTER 7: Using Python constructs in templates =============================================== Since Breve templates *are* Python, you are free to use Python code within them with a single (but important) limitation: Breve templates are expressions and since Python doesn't allow embedding statements within expressions, no Python statements are allowed. A) Statements vs expressions ---------------------------- Python is an imperative language. This means that it makes a distinction between statements and expressions. Logically, there is no real distinction between a statement and an expression (all statements could be expressions if the language allowed it, and many languages do, Lisp and Ruby being two notable ones). Ultimately, the distinction boils down to a single rule: expressions can be embedded in other expressions, statements cannot. Because of this, expressions return a value (although sometimes it is None), statements do not return a value. It's probably best to simply show some statements and expressions and let your intuition do the rest. Examples of statements: ----------------------- if ( x == 1 ): pass for x in range ( 10 ): pass x = 6 def f ( x ): pass Examples of expressions: ------------------------ 3 + 2 x == 1 f ( x ) B) Some concrete examples ------------------------- Here's some examples of Python code embedded in Breve templates to accomplish common tasks. Just remember that, like the conditional expressions mentioned earlier, it's best to keep logic in the template to a minimum. 1) Using a list as a conditional -------------------------------- div [ [ 'goodbye', 'hello' ][ bool ( logged_in ) ] ] 2) List comprehensions as loops ------------------------------- ul [ [ li ( s ) for s in items ] ] 3) Dictionaries as selectors ---------------------------- div [ { 1: span [ 'One' ], 2: span [ 'Two' ], 3: span [ 'Three' ] } [ var ] ] And of course you are free to pass functions, objects and variables into the template and *use* them freely. You simply can't *define* them there. You must define them externally to the template (i.e. in your controller) and pass the object in. As an aside, it is entirely possible to implement a goodly portion of Python's statements as expressions. This has been done at least once [4]. If you truly feel the need for rich logic constructs in your templates you could import a module such as this and pass it into the template's namespace (much the same way custom tags can be passed in, which we'll discuss in a later chapter). CHAPTER 8: Putting it all together ================================== One of the biggest stumbling blocks when learning to use Breve is changing how you think about template organization. Traditional templates with rich logic features built in make it quite easy to paste together a template without much thought. However they also make it quite easy to create templates that appear to have been put together without much thought. Templates are code and we feel they should be given as much care as any other piece of code, especially since debugging HTML and CSS accounts for so much time in website design. For many of the same reasons the more complicated approach of tableless CSS layout is preferable to the easier-to-write table-based layouts of yore, the slightly more difficult approach of Breve pays off in the long run (by "difficult" I mean "requires forethought to get optimal results"). Despite requiring more forethought, there are several patterns that can be readily adopted to make your life with Breve simpler. Pattern 1: the multi-faceted site --------------------------------- A common architecture of sophisticated websites is to have two (or more) views of the same site depending on certain conditions (most often based upon the visitor's status: anonymous, registered, paying/premium, administrator, etc). Breve's inheritance model is ideal for this architecture. Also custom flatteners and renderers can be a great boon. Let's consider a simple site that has two facets: anonymous ("guest") and registered ("user"). Depending on the user's status, many aspects of the site will differ. We'll start with the following templates: # index.b html [ body [ slot ( 'greeting' ), slot ( 'sidebar' ), slot ( 'content' ) ] ] # guest.b inherits ( 'index' ) [ override ( 'greeting' ) [ span [ 'Welcome, guest. ', 'Please ', a ( href = '/login' ) [ 'login' ], ' if you have an account.' ] ], override ( 'sidebar' ) [ ul [ [ li [ m ] for m in guest_menus ] ], include ( 'advertisements' ) ], override ( 'content' ) ] # user.b inherits ( 'index' ) [ override ( 'greeting' ) [ 'Welcome back, %s!' % user.name, 'You have %d new messages' % len ( user.messages ) ], override ( 'sidebar' ) [ ul [ [ li [ m ] for m in user_menus ] ] ], override ( 'content' ) ] Now, you can see we've managed to dynamically change aspects of the site by simple organization rather than by embedding logic within the templates. We *could* have had a single, longer template with conditionals for selecting output, but that would have gotten messy pretty fast. One thing to note is that if an "override" directive doesn't have a body, then the "slot" it overrides will effectively disappear from the final HTML output. In all the examples so far, we've hard-coded URLs directly into the template. Often this isn't desirable since URLs may change over time (or depend upon which facet of the site the visitor is viewing). Many frameworks use a dispatching system to map URLs to to views (for example, Pylons [5] uses Routes [6]) and we want to be able to integrate this mapping down to the template level. This is a place where custom renderers can come in handy. We'll change the guest.b template from the example above to leverage a custom renderer for the login URL: # controllers.py def render_login_url ( tag, data = None ): return tag ( href = '/login' ) [ 'login' ] @view ( template = 'breve:guest' ) def index ( self ): return ( dict ( render_login_url = render_login_url ) ) # guest.b inherits ( 'index' ) [ override ( 'greeting' ) [ span [ 'Welcome, guest. ', 'Please ', a ( render = render_login_url ), ' if you have an account.' ] ], # ... ] There's no hard-and-fast rule for when things should be coded directly into a template or handed off to the controller, but usually if you are finding it difficult to express something in the template then it probably belongs in the controller (either using a custom renderer or flattener). For more information, see the "examples" directory that comes with Breve. Happy hacking! References ========== [1] http://www.divmod.org [2] http://www.wxwidgets.org [3] http://www.turbogears.org [4] http://cheeseshop.python.org/pypi/statements [5] http://pylonshq.com [6] http://pylonshq.com/docs/0.9.3/module-routes.html