wiki:RendererObjects

The Renderer objects

Creating a renderer

As described in ComponentModel, the views of a component received a renderer. A renderer is a DOM objects factory that the developer uses to build the DOM tree of the view (see PresentationTier):

from nagare import presentation

class App:
    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 views receive a new XHTML renderer (instance of the Renderer class of namespaces/xhtml.py#xhtml.Renderer) suitable to build a synchronous (X)HTML DOM tree.

A view can also create a different renderer, for example to generate an other XML dialect than XHTML:

from nagare import presentation
from nagare.namespaces import xml

class App:
    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 = xhtml.AsyncRenderer(h)
    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.

Default renderers

XHTML renderer

A XHTML renderer is an instance of the Renderer class of namespaces/xhtml.py#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 (choosen 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 the class HeadRenderer of namespaces/xhtml.py#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 (see ApplicationConfiguration)

    @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 (see ApplicationConfiguration)

    @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.

XML renderer

A XML renderer is an instance of the Renderer class of namespaces/xml.py#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 the Renderer class of namespaces/xsl.py#xsl.Renderer and has a factory for all the possible XSL tags.

ESI renderer

A ESI renderer is an instance of the Renderer class of namespaces/esi.py#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 combinated 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>