The Renderer objects

Creating a renderer

As described in The Nagare component model, the views of a component receives a renderer. A renderer is a DOM objects factory that the developer uses to build the DOM tree of the view:

from nagare import presentation

class App(object):
    pass

@presentation.render_for(App):
def render(self, h, *args):
    # the `h` parameter is the renderer, used to generate a DOM tree
    return h.div(h.h1('Hello world!'))

By default, each view receives a new XHTML renderer (instance of nagare.namespaces.xhtml.Renderer) suitable to build a synchronous (X)HTML DOM tree.

A single view can also use a different renderer, for example to generate another XML dialect than XHTML:

from nagare import presentation
from nagare.namespaces import xml

class App(object):
    pass

@presentation.render_for(App):
def render(self, h, *args):
    h.response.content_type = 'text/xml'

    x = xml.Renderer(h)    # Creating a XML renderer
    return x.contact(x.name('John Doe'), x.age(20))

or to generate an asynchronous view of a component:

from nagare import presentation, component
from nagare.namespaces import xhtml
from MyBlog import Blog

class App:
    def __init__(self):
        self.content = component.Component(Blog())

@presentation.render_for(App):
def render(self, h, *args):
    # Creating an asynchronous XHTML renderer
    a = h.AsyncRenderer()
    return h.div(self.content.render(a))

Note

The new renderer must be created with the current renderer as first parameter, to maintain a parent/child relationship between them.

Now, if all your views need to generate XML (or use any other renderer), it would be cumbersome to create a renderer in each view. Instead, you can tell Nagare to use another default renderer by setting the renderer_factory attribute of the WSGIApp, as shown in the example below:

from nagare import wsgi
from nagare.namespaces import xml

class WSGIApp(wsgi.WSGIApp):
    # use the XML renderer by default
    renderer_factory = xml.Renderer

Then, all the views will receive an instance of xml.Renderer as renderer. Note that you can still create another renderer in the view as explained above.

Default renderers

XHTML renderer

A XHTML renderer is an instance of nagare.namespaces.xhtml.Renderer.

A XHTML renderer is a factory for all the possible XHTML tags. It also has:

  • a request and a response attributes which are WebOb request and response objects.
  • a head attribute which is a Head renderer instance, see below.
  • the following methods:
    • parse_html(self, source, fragment=False, no_leading_text=False, xhtml=False) – to read HTML or XHTML (chosen with the xhtml parameter) from the source file to a DOM tree. If fragment is True, the (X)HTML read can have multiple roots and the result will always be a list of DOM trees. Also, if fragment is True, if no_leading_text is True and the (X)HTML begins by a text instead of a node, this text is removed.
    • parse_htmlstring(self, text, fragment=False, no_leading_text=False, xhtml=False) – as parse_html() but the (X)HTML is read from the text string.

Head renderer

The head attribute of a XHTML renderer is a Head renderer, an instance of nagare.namespaces.xhtml.HeadRenderer.

A head renderer is a factory for all the XHTML tags that can be embedded into the <head> section: base, head, link, meta, title, style and script:

from nagare import presentation

class App:
    pass

@presentation.render_for(App):
def render(self, h, *args):
    # h.head is a Head renderer
    h.head << h.head.title('My Application')

    return h.div(h.h1('Hello world !'))

A head renderer has also the following method:

  • css(self, name, style) – add a css style definition. The style must be named so that, if added by several views, it will be included only once in the generated page.

    @presentation.render_for(App):
    def render(self, h, *args):
        h.head.css('main_content', '.main { border: 1px red solid }')
    
        return h.div(h.h1('Hello world!'), class_='main')
    
  • css_url(self, url) – add a css url. If the url is relative, it’s relative to the static directory of the application.

    @presentation.render_for(App):
    def render(self, h, *args):
        h.head.css_url('http://www.nagare.org/site.css') # Absolute URL
        h.head.css_url('css/my_application.css')         # Relative URL
    
        return h.div(h.h1('Hello world!'), class_='main')
    
  • javascript(self, name, script) – add a javascript definition. The javascript code must be named so that, if added by several views, it will be included only once in the generated page.

    @presentation.render_for(App):
    def render(self, h, *args):
        h.head.javascript('debug', 'function debug(msg) { alert(msg) }')
    
        return h.div(h.h1('Hello world!'), class_='main')
    
  • javascript_url(self, url) – add a javascript url. If the url is relative, it’s relative to the static directory of the application

    @presentation.render_for(App):
    def render(self, h, *args):
        h.head.javascript_url('http://www.nagare.org/anim.js') # Absolute URL
        h.head.javascript_url('js/debug.js')                   # Relative URL
    
        return h.div(h.h1('Hello world!'), class_='main')
    

After the rendering phase, Nagare generates a <head> section which is the concatenation of all the DOM objects the different views have put into the Head renderer.

XHTML5 renderer

A XHTML5 renderer is an instance of nagare.namespaces.xhtml5.Renderer.

A XHTML5 renderer is a factory for all the possible HTML5 or XHTML5 tags from the HTML5 specification.

Since it’s extremely likely that you want to use this renderer in all the views of your application instead of the default (X)HTML renderer, you can install it like this:

from nagare import wsgi
from nagare.namespaces import xhtml5

class WSGIApp(wsgi.WSGIApp):
    # use the (X)HTML5 renderer by default
    renderer_factory = xhtml5.Renderer

Then, all the views will receive a (X)HTML5 renderer by default.

This renderer is like an XHTML renderer except that the set of available tags and attributes is a little different. For example, HTML5 offers <audio>, <video> and <canvas> tags and more structural markup such as <section>, <article>, <aside>, <nav>, <figure>, <header> and <footer> tags.

XML renderer

A XML renderer is an instance of nagare.namespaces.xml.Renderer.

It’s a simple renderer which accepts any tag creation:

>>> from nagare.namespaces import xml

>>> x = xml.Renderer()
>>> tree = x.contact(x.name('John Doe'), x.age(20))

>>> tree.write_xmlstring()
'<contact><name>John Doe</name><age>20</age></contact>'

XSL renderer

A XSL renderer is an instance of nagare.namespaces.xsl.Renderer and has a factory for all the possible XSL tags.

ESI renderer

A ESI renderer is an instance of nagare.namespaces.esi.Renderer and has a factory for all the possible ESI tags : attempt, choose, comment, include, inline, otherwise, remove, try, vars, when.

Namespaces and renderers mixin

A renderer can create DOM object in a specific XML namespace. First the namespace(s) must be declared to the renderer by setting its namespaces attribute to a namespace prefix -> namespace uri dictionary. Then, at any moment, the namespace to use is choosen by setting its default_namespace atttribut to the prefix of the namespace:

>>> from nagare.namespaces import xsl

>>> x = xsl.Renderer()
>>> x.namespaces = {'xsl': 'http://www.w3.org/1999/XSL/Transform' }
>>> x.default_namespace = 'xsl'

>>> xsl = x.stylesheet(
...           x.output(encoding='utf-8'),
...           x.template(
...               x.copy_of(select='.'),
...               match='/'
...           )
...       )

>>> print xsl.write_xmlstring(pretty_print=True)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output encoding="utf-8"/>
  <xsl:template match="/">
    <xsl:copy-of select="."/>
  </xsl:template>
</xsl:stylesheet>

Different renderers, with different namespaces, can be combined to build a DOM tree:

>>> from nagare.namespaces import xhtml, esi

>>> h = xhtml.Renderer()

>>> s = esi.Renderer()
>>> s.namespaces = {'esi': esi.NS }
>>> s.default_namespace = 'esi'

>>> tree = h.html(
...            h.body(
...                h.h1('Hello'),
...                s.include(src='http://www.nagare.org'),
...                h.p('world')
...            )
...        )
>>> print tree.write_xmlstring(pretty_print=True)
<html>
  <body>
    <h1>Hello</h1>
    <esi:include xmlns:esi="http://www.edge-delivery.org/esi/1.0" src="http://www.nagare.org"/>
    <p>world</p>
  </body>
</html>