Posts in category presentation

Nagare Callbacks, Iterations And The Mighty Default Parameter

Problem

Consider the following Python code:

>>> funcs = []
>>> for a in [1,2,3]:
...     funcs.append(lambda:a)
...
>>> print [f() for f in funcs]
[3, 3, 3]

Here, I created three anonymous functions, which return the value of a. Intuitively, we could think that when evaluating these functions, they would return the value a had when it was referenced during the function creation. Instead, all three functions return the last value of a. Why ?

The lambda expression captured a reference to the outside-defined scope that holds the variable a. This reference is used when evaluating the lambda expression. Normally, a wouldn't exist outside the loop, but lambda expressions captured a reference to it. Too bad, it evaluates to the last value it had.

How can we solve this problem ?

In Python, "Default parameter values are evaluated when the function definition is "executed" (see Python reference documentation).

We can use this behaviour to freeze the value of the iteration variable in our lambda expression.

Example:

>>> funcs = []
>>> for a in [1,2,3]:
...     funcs.append(lambda a=a:a)
...
>>> print [f() for f in funcs]
[1, 2, 3]

Application to Nagare

In Nagare, you often create a lambda expression as a callback to an action. For example:

h << h.input(type='submit', value='Cancel').action(lambda: comp.answer())

Here, comp is referenced in the lambda expression. It's ok because comp is not an iteration variable.

But let's take a look at the main view of the "Tic Tac Toe" example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@presentation.render_for(TicTacToe)
def default_view(self, h, comp, *args):
    with h.table:
        i = 0
        for row in zip(self._board[::3], self._board[1::3], self._board[2::3]):
            with h.tr:
                for cell in row:
                    with h.td:
                        if cell == 0:
                            h << h.a('-').action(lambda i=i: comp.answer(i))
                        else:
                            h << ('X' if cell==1 else 'O')
                        i += 1
    return h.root

In this view, there is an iteration to create the board, and for each link, we would like the associated callback to answer the value of i, i.e. the id of the clicked cell! To achieve that, you can see that we put a default argument value on the lambda (line 10).

So remember to use the Mighty Default Parameter when defining Callbacks in an Iteration!

Use Nagare namespaces to create an rss feed

In Nagare HTML and XML generation is done through Renderers. Thus, to generate RSS feeds it is possible to use Nagare XML Renderer, which accept any tag or attribute. Another way is to create a RSS Renderer for Nagare which matches RSS namespace.

To do so we are extending Nagare XmlRender, which enforces possible xml tags and attributes (e.g. namespace).

Simple RSSRenderer

RSS specification, leads to the following namespace definition in Nagare:

class RssRenderer(xml.XmlRenderer):
    """RSS renderer"""
    # RSS tags
    # ------------
    rss = TagProp('rss', set(('version', )))
    channel = TagProp('channel', set())
    title = TagProp('title', set())
    link = TagProp('link', set())
    description = TagProp('description', set())
    language = TagProp('language', set())
    copyright = TagProp('copyright', set())
    managingEditor = TagProp('managingEditor', set())
    webMaster = TagProp('webMaster', set())
    pubDate = TagProp('pubDate', set())
    lastBuildDate = TagProp('lastBuildDate', set())
    category = TagProp('category', set())
    generator = TagProp('generator', set())
    docs = TagProp('docs', set())
    cloud = TagProp('cloud', set(('domain', 'port',
        'path', 'registerProcedure', 'protocol')))
    ttl = TagProp('ttl', set())
    image = TagProp('image', set())
    url = TagProp('url', set())
    width = TagProp('width', set())
    height = TagProp('height', set())
    rating = TagProp('rating', set())
    textInput = TagProp('textInput', set())
    name = TagProp('name', set())
    skipHours = TagProp('skipHours', set())
    skipDays = TagProp('skipDays', set())
    item = TagProp('item', set())
    title = TagProp('title', set())
    link = TagProp('link', set())
    description = TagProp('description', set())
    author = TagProp('author', set())
    category = TagProp('category', set(('domain', )))
    comments = TagProp('comments', set())
    enclosure = TagProp('enclosure', set(('url', 'length', 'type')))
    guid = TagProp('guid', set(('isPermaLink', )))
    pubDate = TagProp('pubDate', set())
    source = TagProp('source', set(('url', )))

Note

Notes:

  • RssRenderer checks if tags and attibutes exist in namespace
  • RssRenderer does not validate against any DTD or schema

Let's look at the following example:

<?xml version="1.0"?>
<rss version="2.0">
    <channel>
        <title>Some Blog Title</title>
        <link>http://someblog.example.com</link>
        <description>Description</description>
        <language>en-us</language>
        <pubDate>Tue, 25 Jan 2010 14:00:00 GMT</pubDate>
        <lastBuildDate>Tue, 25 Jan 2010 14:00:00 GMT</lastBuildDate>
        <docs>http://blogs.law.harvard.edu/tech/rss</docs>
        <generator>Some Generator</generator>
        <managingEditor>editor@example.com</managingEditor>
        <webMaster>webmaster@example.com</webMaster>
        <item>
            <title>Some Post Title</title>
            <link>http://someblog.example.com/post1</link>
            <description>Some Post Description</description>
            <pubDate>Tue, 25 Jan 2010 14:00:00 GMT</pubDate>
            <guid>Some Global Unique Identifier</guid>
        </item>
    </channel>
</rss>

With RssRenderer, this feed is generated this way:

r = rss.RssRenderer()
with r.rss(version="2.0"):
    with r.channel:
        r << r.title('Some Blog Title')
        r << r.link('http://someblog.example.com')
        r << r.description('Description')
        r << r.language('en-us')
        r << r.pubDate('Tue, 25 Jan 2010 14:00:00 GMT')
        r << r.lastBuildDate('Tue, 25 Jan 2010 14:00:00 GMT')
        r << r.docs('http://blogs.law.harvard.edu/tech/rss')
        r << r.generator('Some Generator')
        r << r.managingEditor('editor@example.com')
        r << r.webMaster('webmaster@example.com')
        with r.item:
            r << r.title('Some Post Title')
            r << r.link('http://someblog.example.com/post1')
            r << r.description('Some Post Description')
            r << r.pubDate('Tue, 25 Jan 2010 14:00:00 GMT')
            r << r.guid('Some Global Unique Identifier')

Now RSSRenderer can be used to generate any RSS feed.

Mixing multiple namespaces

There's several extensions to RSS, actually extensions are just XML namespaces added in the feed. One simple extension is blogChannel namespace.

First create a BlogChannelRenderer:

class BlogChannelRenderer(xml.XmlRenderer):
    blogRoll = TagProp('blogRoll', set())
    mySubscriptions = TagProp('mySubscriptions', set())
    blink = TagProp('blink', set())
    changes = TagProp('changes', set())

Now we can mix RssRenderer and BlogChannelRenderer to create one feed:

r = rss.RssRenderer()
r.namespaces = {'blogChannel': 'http://backend.userland.com/blogChannelModule'}
b = rss.BlogChannelRenderer(r)
b.default_namespace = 'blogChannel'
with r.rss(version="2.0"):
    with r.channel:
        r << r.title('Some Blog Title')
        r << r.link('http://someblog.example.com')
        r << r.description('Description')
        r << r.language('en-us')
        r << b.blogRoll('http://someblog.example.com/blogroll')
        r << b.mySubscriptions('http://someblog.example.com/subscriptions')
        r << b.blink('http://someblog.example.com/backlink')
        r << b.changes('http://someblog.example.com/changes')
        r << r.pubDate('Tue, 25 Jan 2010 14:00:00 GMT')
        r << r.lastBuildDate('Tue, 25 Jan 2010 14:00:00 GMT')
        r << r.docs('http://blogs.law.harvard.edu/tech/rss')
        r << r.generator('Some Generator')
        r << r.managingEditor('editor@example.com')
        r << r.webMaster('webmaster@example.com')
        with r.item:
            r << r.title('Some Post Title')
            r << r.link('http://someblog.example.com/post1')
            r << r.description('Some Post Description')
            r << r.pubDate('Tue, 25 Jan 2010 14:00:00 GMT')
            r << r.guid('Some Global Unique Identifier')

This produces the following RSS feed:

<rss xmlns:blogChannel="http://backend.userland.com/blogChannelModule" version="2.0">
  <channel>
    <title>Some Blog Title</title>
    <link>http://someblog.example.com</link>
    <description>Description</description>
    <language>en-us</language>
    <blogChannel:blogRoll>http://someblog.example.com/blogroll</blogChannel:blogRoll>
    <blogChannel:mySubscriptions>http://someblog.example.com/subscriptions</blogChannel:mySubscriptions>
    <blogChannel:blink>http://someblog.example.com/backlink</blogChannel:blink>
    <blogChannel:changes>http://someblog.example.com/changes</blogChannel:changes>
    <pubDate>Tue, 25 Jan 2010 14:00:00 GMT</pubDate>
    <lastBuildDate>Tue, 25 Jan 2010 14:00:00 GMT</lastBuildDate>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>Some Generator</generator>
    <managingEditor>editor@example.com</managingEditor>
    <webMaster>webmaster@example.com</webMaster>
    <item>
      <title>Some Post Title</title>
      <link>http://someblog.example.com/post1</link>
      <description>Some Post Description</description>
      <pubDate>Tue, 25 Jan 2010 14:00:00 GMT</pubDate>
      <guid>Some Global Unique Identifier</guid>
    </item>
  </channel>
</rss>

With this process any XML namespace can be added to Nagare. For example, Nagare XHTML namespace is implemented as a Renderer.

Common HTML structuration for different components

The problem

We want to obtain the same HTML structure when rendering different components.

Templating using inheritance and named views

The principle is the same than the template method design pattern but applied to components views.

Setting the base class

Let's declare an empty base class:

class BaseEntry(object):
    pass

Its default view sets the common HTML structure and embeds the title and properties named views:

from nagare.presentation import render_for
@render_for(BaseEntry)
def render_base_entry(self, h, comp, *args):
    """All the views of entries will have this HTML structure"""
    with h.div(class_='entry'):
        with h.div(class_='title'):
            h << comp.render(h, model='title')
        with h.div(class_='properties'):
            h << comp.render(h, model='properties')
    return h.root

The BaseEntry's title and properties views are empty. They will be redefined for the derived classes:

@render_for(BaseEntry, model='title')
def render_base_entry__header(self, h, *args):
    return h.root
@render_for(BaseEntry, model='properties')
def render_base_entry__properties(self, h, *args):
    return h.root

We can now verify that the basic HTML structure is generated:

>>> from nagare import component
>>> from nagare.namespaces import xhtml
>>>
>>> entry = component.Component(BaseEntry())
>>> h = xhtml.Renderer()
>>> print entry.render(h).write_htmlstring(pretty_print=True)
<div class="entry">
<div class="title"></div>
<div class="properties"></div>
</div>

Using the base class

Now, let's create a derived class:

class SimpleEntry(BaseEntry):
    def __init__(self, name, firstname, age):
        self.name = name
        self.firstname = firstname
        self.age = age

We only have to define its title and properties views:

@render_for(SimpleEntry, model='title')
def render_simple_entry__title(self, h, *args):
    return '%s %s' % (self.firstname, self.name)
@render_for(SimpleEntry, model='properties')
def render_simple_entry__properties(self, h, *args):
    return h.div('Age: %d' % self.age)

for the component data to be correctly inserted into the right HTML structure:

>>> from nagare import component
>>> from nagare.namespaces import xhtml
>>>
>>> entry = component.Component(SimpleEntry('John', 'Doe', 20))
>>> h = xhtml.Renderer()
>>> print entry.render(h).write_htmlstring(pretty_print=True)
<div class="entry">
<div class="title">Doe John</div>
<div class="properties"><div>Age: 20</div></div>
</div>

Templating using aggregation and named views

When inheritance is not wanted, we can define a component whose job is to do the rendering:

class EntryTemplate(object):
    def __init__(self, entry):
        self.entry = entry
@render_for(EntryTemplate)
def render_entry_template(self, h, *args):
    """
    Same code that we used in ``render_BaseEntry``
    but using ``self.entry`` instead of ``self``.
    """
    with h.div(class_='entry'):
        with h.div(class_='title'):
            h << self.entry.render(h, model='title')
        with h.div(class_='properties'):
            h << self.entry.render(h, model='properties')
    return h.root

Let's use it with our SimpleEntry class:

class SimpleEntry(object): # no inheritance anymore
    def __init__(self, name, firstname, age):
        self.name = name
        self.firstname = firstname
        self.age = age

The title and properties views are still the same, but we must now declare a default view to wrap our SimpleEntry component into a EntryTemplate component and render it:

from nagare.component import Component
@render_for(SimpleEntry)
def render_simple_entry(self, h, comp, *args):
    return Component(EntryTemplate(comp)).render(h)

Then we can use the SimpleEntry components exactly like in the previous chapter:

>>> from nagare import component
>>> from nagare.namespaces import xhtml
>>>
>>> entry = component.Component(SimpleEntry('John', 'Doe', 20))
>>> h = xhtml.Renderer()
>>> print entry.render(h).write_htmlstring(pretty_print=True)
<div class="entry">
<div class="title">Doe John</div>
<div class="properties"><div>Age: 20</div></div>
</div>