Woven models and the request object

It has irritated me for a while now that the interfaces on IModel don't take the request object as the first argument. When I originally wrote IModel, it was written in the context of "here is a general MVC module, and Woven will build upon this", but that turned out to be more trouble than it was worth -- there's no sense separating things out when you will never use MVC standalone. So the other interfaces, IView and IController, which were already considered web-aware, had access to the request, but not IModel.

I have been aware for some time now that IModel is an interface that is about preparing a model for display in a web browser -- much like Zope3's IView. It converts from whatever raw object interface is present on the original model, to an interface which exposes data suitable for traversal and insertion into a web template file.

So, tonight I went through and added the resource object as the first parameter to lookupSubmodel, getSubmodel, setSubmodel, getData, and setData. Changing the signature of methods which are widely used in a wide variety of situations is painful -- especially when you're changing an interface which is as fundamental to a system as IModel is to woven. However, thanks to the inspect module and some fancy use of positional optional arguments, I was able to come up with a solution that logs DeprecationWarnings whenever a method doesn't expect the request, and passes the request when a method does.


Whoops. I left out a

Whoops. I left out a step that is required to get FlashConduit working properly in the post below. You need to pass --flashconduit 4321 to mktap web to make the FlashConduit server side component listen on port 4321, like this:

mktap web --path=/path/to/web/files --flashconduit=4321

I'll edit the post below to mention this.


For people who are curious

For people who are curious about the bleeding edge of Woven development, I have been working on LivePage a bit more recently. LivePage allows asynchronous events both from the browser, and to the browser after the page has already been loaded. The current implementation in CVS requires Flash, but it is also technically possible to get this to work with an IFrame connection that the server never closes as the Output Conduit. I whipped up a little example so you can get a feel for where I am going with this, and give me feedback if you desire.

If you want to try this, save it as an rpy and view it with twisted.web using a tap created like this: (Latest CVS is probably required)

mktap web --path=/path/to/web/resources --flashconduit 4321

Make sure you put a slash after the .rpy, so the relative urls spit out by the webConduitGlue resolve correctly. For example, http://localhost:8080/simpleLivePageExample.rpy/

from twisted.web.woven import model, page, input, widgets

class ANumber(model.AttributeModel):
def __init__(self):
self.value = 0

class AWidget(widgets.Widget):
def setUp(self, request, node, data):

class HandleInput(input.Anything):
def initialize(self):
self.view.addEventHandler("onclick", self.clicked)

def clicked(self, request, widget):
print "CLICKED!"
self.model.setData(self.model.getData() + 1)
self.model.notify({'request': request})

resource = page.LivePage(ANumber())

resource.setSubviewFactory('aView', AWidget)
resource.setSubcontrollerFactory('aController', HandleInput)

resource.template = """<html>
<div model="value" view="aView">

<img src="http://www.google.com/images/logo.gif" model="value" view="Widget" controller="aController" />

<div view="webConduitGlue" />