[Twisted-web] new nevow know-how needs...;-)

James Y Knight twisted-web@twistedmatrix.com
Thu, 4 Mar 2004 20:40:34 -0500


On Mar 1, 2004, at 6:02 AM, Alex Martelli wrote:
> I currently have _one_ serious problem understanding what's going on 
> (or
> supposed to go on): nevow:data="whatever" directives.  I don't 
> understand
> the rules regarding WHEN this causes a lookup (e.g. of 'data_whatever' 
> on a
> rend.Page subclass), WHAT it looks things up when it does do a lookup,
> WHEN it calls method 'data_whatever' thereafter, and with what 
> arguments.
> Basically, I must be missing something important here -- it mostly 
> feels
> that once I've used a nevow:data= directive, I lose control of what's 
> going
> to happen and get nasty surprises.  So, I'm gradually moving to using
> nevow.render= directives instead -- mixing the "model" aspect (getting 
> the
> data on which rendering is to be based) with the "view" one (rendering 
> it).
> Not nice, but with nevow.render I _do_ appear to have complete 
> control, or
> maybe better expressed, _understanding_, of what's gonna happen...

I just totally changed how this worked today. I'll describe what *now* 
happens, which is different from what used to happen:

def data_foo(context, data):
   return 4
def render_foo(context, data):
   return context.tag['Hi: '+data]
span(data=data_foo, renderer=render_foo)

This will first call data_foo, returning '4'. data_foo's context will 
be the context of the span (context.tag is the span tag), without its 
IData remembered yet (thus, the data argument will be whatever the 
containing data is, in most cases, the rend.Page instance.

Then, render_foo will be called, with the tag's context, again, but now 
with the result of data_foo's result (4) as 'data'.
Thus, this will result in the string "<span>Hi: 4</span>".

Now, take another example:
number = 0
def data_count(context, data):
   global number
   number = number + 1
   return number

span(data=data_count)[str, ' ', str, ' ', str], ' ', 
span(data=data_count)[str, ' ', str, ' ', str]

This will result in the string "<span>1 1 1</span> <span>2 2 2</span>".

Note that returning deferreds from the data_ is now supported. In 
detail, the way this works:
nevow.flat.flatstan.TagRenderer calls (quite paraphrased) 
"context.remember(convertToData(data, context), IData)" when it first 
sees a tag, right before it calls the function specified by the render= 
attribute.

convertToData causes functions to be called, 'directive' statements to 
be looked up, and deferreds to be handled. Then, when the actual 
rendering function is called, it does context.locate(IData), which 
finds the previously remembered data, and passes it directly (no 
further fiddling is done!) to the data argument of the renderer.

If you use a directive('name') form in a data=, this causes the current 
data to be adapted to IContainer before calling .get on it. There are 
currently adapters for dict, list, and tuple for this.
Thus,
def data_foo(context, data):
   return defer.succeed({'foo':1, 'bar':2})

span(data=data_foo)[span(data=directive('foo'))[str], 
span(data=directive('bar')[str]]

Should work as expected, and call data_foo *once*, and produce 
"<span><span>1</span><span>2</span></span>".

Hope this helps,
James