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 aresponse
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 thexhtml
parameter) from thesource
file to a DOM tree. Iffragment
isTrue
, the (X)HTML read can have multiple roots and the result will always be a list of DOM trees. Also, iffragment
isTrue
, ifno_leading_text
isTrue
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)
– asparse_html()
but the (X)HTML is read from thetext
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 thestatic
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 thestatic
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>