Tuesday, February 27, 2007

Object Publishing with Nevow

Update: Code for the "Nevow Object Series" of blog posts is available here. Note that the examples can be viewed and run as single files or "project style" with code broken out into separate files.

Object publishing is a very simple concept: making code objects accessible over some medium. The medium most often discussed, due to the prevalence of web applications is HTTP/S. For our purposes, the published objects we will discuss are instantiations of python classes written to provide resources over the network.

In the python community, the most famous example of object publishing is the Zope application server. Zope exposes object written in python or with various Zope scripts/products/etc. In an old Zope 2 Book, Zope's object publishing is broken down very simply:
  1. The client sends a request to the server
  2. The server locates the object using the request URL as a map
  3. The server calls the published object with arguments from the request.
  4. The server interprets and returns the results to the client
What we have here is essentially object look-ups by URL. Given the web context, this makes obvious sense.

With such a simple definition of "object publishing", we can begin seeing "object publishing" pretty much anywhere python is used to deliver content or other resources on the web. For instance, Django qualifies as having an object publisher by means of the configuration mechanism whereby one can use regular expressions to map URLs to its "view" objects. We'll come back to Django in a bit.

Twisted's web "framework" Nevow has a object-publishing capabilities that, due to it being controlled by python classes is limited only by the python code you can write. There are four primary mechanisms by which one can publish objects in Nevow:
  • child attributes on a resource
  • child methods on a resource, or
  • dynamic children using the childFactory() method
  • manipulating the traversal of objects via locateChild() method
I will be focusing on the first two, however, before showing examples of these let's cover some minimal background. Twisted services are commonly run using what's called a .tac file ("Twisted Application Configuration"). Here is a trivial example of this:
from twisted.application import service
from twisted.application import strports

from nevow import appserver

import stuff

# instantiate the application
application = service.Application('Site')

# set up the main resource
root = mystuff.SiteRoot()
site = appserver.NevowSite(root)

# setup the web server
server = strports.service('tcp:8080', site)
server.setServiceParent(application)
Give than this file was saved as site.tac (and that there was a sibling file named stuff.py with the resource SiteRoot defined), you would enter twistd -noy site.tac at the system prompt to run this. This would start a server listening on port 8080. But what is SiteRoot? It is a resource we are making available at the root of our application, "publishing" it at http://localhost:8080/. And what would it look like, you ask? Well, a silly example of stuff.py that define SiteRoot might be something like the following:
from nevow import rend
from nevow import loaders

class SiteRoot(rend.Page):

docFactory = loaders.xmlstr('''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"
xmlns:nevow="http://nevow.com/ns/nevow/0.1">
</html>''')
One reason that this is a silly example is that it does nothing other than declare that the published resource will be an XHTML file with a Nevow namespace, providing no methods (and little content). Additionally, it uses the XML "string" loader, instead of using stan or the XML file loader (for using templates from disk). Regardless, if we add a child attribute that points to another class, we can start to see the object publishing behavior of Nevow:
from nevow import rend
from nevow import loaders

class MyStuffResource(rend.Page):

docFactory = loaders.xmlstr('''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"
xmlns:nevow="http://nevow.com/ns/nevow/0.1">
<h1>My Stuff</h1>
</html>''')

class SiteRoot(rend.Page):

docFactory = loaders.xmlstr('''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"
xmlns:nevow="http://nevow.com/ns/nevow/0.1">
</html>''')

child_mystuff = MyStuffResource()
Starting up our application with these changes will allow us to access the root and child object via the following URLs, respectively:
  • http://localhost:8080/
  • http://localhost:8080/mystuff
This demonstrates how to publish objects via the child_ attribute. child_*() methods do the same thing, while offering all the flexibility that comes with methods. For instance, we could define the method child_yourstuff(self, context) in SiteRoot that would check for your username in the application cookie and pass that info when instantiating a UserStuffResource class. Then the URL http://localhost:8080/yourstuff could publish data specific to whatever was available in a data source associated with the username. The child_*() methods do need to return a resource object, though.

This has been a brief run-down on how you can use child_*() methods and child_ attributes to publish objects. In the next installment, I will show how to customize URL traversal for accessing these resources via the locateChild() method. In particular, I will show how to duplicate the Django URL conf functionality in Nevow.

Technorati Tags: , , ,

No comments: