[Checkins] SVN: z3c.formjs/trunk/src/z3c/formjs/ajax. Made AJAX
feature thread-safe.
Stephan Richter
srichter at cosmos.phy.tufts.edu
Thu Aug 23 15:07:38 EDT 2007
Log message for revision 79168:
Made AJAX feature thread-safe.
Changed:
U z3c.formjs/trunk/src/z3c/formjs/ajax.py
U z3c.formjs/trunk/src/z3c/formjs/ajax.txt
-=-
Modified: z3c.formjs/trunk/src/z3c/formjs/ajax.py
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/ajax.py 2007-08-23 19:06:37 UTC (rev 79167)
+++ z3c.formjs/trunk/src/z3c/formjs/ajax.py 2007-08-23 19:07:38 UTC (rev 79168)
@@ -38,27 +38,28 @@
def __repr__(self):
return "<%s %r>" % (self.__class__.__name__, self.keys())
-class AJAXRequestHandler(object):
- zope.interface.implements(interfaces.IAJAXRequestHandler,
- interfaces.IFormTraverser)
- ajaxRequestHandlers = AJAXHandlers()
-
-
-class AJAXHandler(BrowserPage):
+class AJAXHandler(object):
zope.interface.implements(interfaces.IAJAXHandler)
- context = None
-
def __init__(self, func):
self.func = func
- def __call__(self):
- return self.func(self.context)
+ def __call__(self, view):
+ return self.func(view)
def __repr__(self):
return "<%s %r>" % (self.__class__.__name__, self.func.__name__)
+
+class AJAXRequestHandler(object):
+ """Mix-in class for forms to support AJAX calls."""
+ zope.interface.implements(interfaces.IAJAXRequestHandler,
+ interfaces.IFormTraverser)
+
+ ajaxRequestHandlers = AJAXHandlers()
+
+
def handler(func):
"""A decorator for defining an AJAX request handler."""
handler = AJAXHandler(func)
@@ -69,6 +70,18 @@
return handler
+class AJAXView(BrowserPage):
+ """A wrapper class around AJAX handler to allow it to be publishable."""
+
+ def __init__(self, handler, request, view):
+ self.context = self.handler = handler
+ self.request = request
+ self.__parent__ = self.view = view
+
+ def __call__(self):
+ return self.handler(self.view)
+
+
class AJAXRequestTraverserPlugin(object):
"""Allow access to methods registered as an ajax request handler."""
@@ -83,5 +96,4 @@
if handler is None:
raise NotFound(self.context, name, request)
- handler.context = self.context
- return handler
+ return AJAXView(handler, self.request, self.context)
Modified: z3c.formjs/trunk/src/z3c/formjs/ajax.txt
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/ajax.txt 2007-08-23 19:06:37 UTC (rev 79167)
+++ z3c.formjs/trunk/src/z3c/formjs/ajax.txt 2007-08-23 19:07:38 UTC (rev 79168)
@@ -2,8 +2,8 @@
AJAX Helpers
============
-The formjs package also sports utilities for working with AJAX
-queries. These utilities are provided by the ``ajax`` module.
+The formjs package also sports utilities for working with AJAX queries. These
+utilities are provided by the ``ajax`` module.
>>> from z3c.formjs import ajax
@@ -11,14 +11,14 @@
AJAX Request Handlers
---------------------
-AJAX requests are sent from a client-side JavaScript script to the web
-server. The request may contain form data or any other request data
-and the server sends back a response based on the request. The
-functionality for handling requests and returning responses is already
-handled by browser views. But browser views can be a bit overkill for
-handling very simple requests and responses that don't necessarily
-involve rendering full page templates. The ``ajax`` module allows you
-to quickly build in short ajax request handlers into your form.
+AJAX requests are sent from a client-side JavaScript script to the web server.
+The request may contain form data or any other request data and the server
+sends back a response based on the request. The functionality for handling
+requests and returning responses is already handled by browser views. But
+browser views can be a bit overkill for handling very simple requests and
+responses that don't necessarily involve rendering full page templates. The
+``ajax`` module allows you to quickly build short ajax request handlers into
+your form.
We will first do the necessary setup steps:
@@ -35,14 +35,13 @@
... @ajax.handler
... def pingBack(self):
... message = self.request.get('message', 'Nothing to ping back.')
- ... return "from %r: %s" % (self.context, message)
+ ... return "from %r: %s" % (self, message)
The ``AJAXRequestHandler`` class provides the ``IAJAXRequestHandler``
-interface. This means that the PingForm class will have an
-``ajaxRequestHandlers`` selection manager. When you use the
-``@ajax.handler`` decorator, the decorated function gets registered
-in the selection manager and is converted to an ``AJAXHandler``
-instance.
+interface. This means that the ``PingForm`` class will have an
+``ajaxRequestHandlers`` selection manager. When you use the ``@ajax.handler``
+decorator, the decorated function gets registered in the AJAX handlers manager
+and is converted to an ``AJAXHandler`` instance.
>>> from z3c.form.testing import TestRequest
>>> request = TestRequest()
@@ -54,44 +53,27 @@
>>> ping.ajaxRequestHandlers['pingBack']
<AJAXHandler 'pingBack'>
-When the ``AJAXHandler`` instance is created, it does not know about
-the context. Thus, the handler function defined in the form will not
-have access to other attributes in the form (including the request)
-unless the context is explicitly set.
+Since the form or anything else of interest is not around during the creation
+of the ``AJAXHandler`` instance, we have to wrap the handler once the other
+components are available. When executing the AJAX handler itself, you must
+pass in the form explicitely:
- >>> ping.pingBack()
- Traceback (most recent call last):
- ...
- AttributeError: 'NoneType' object has no attribute 'request'
+ >>> print ping.pingBack(ping)
+ from <PingForm ...>: Nothing to ping back.
-So we need to set the context for the handler before we call it.
+Before the handler is called we put an ``AJAXView`` class around it:
- >>> ping.pingBack.context = ping
- >>> ping.pingBack()
- 'from None: Nothing to ping back.'
+ >>> pingView = ajax.AJAXView(ping.pingBack, ping.request, ping)
-Since the function is now a publishable object, it provides the
-``IBrowserPublisher`` interface
+The ping AJAX view can simply be called:
- >>> from zope.publisher.interfaces.browser import IBrowserPublisher
- >>> IBrowserPublisher.providedBy(ping.pingBack)
- True
+ >>> print pingView()
+ from <PingForm ...>: Nothing to ping back.
-All of this machinery is best handled by a pluggable traverser. First
-we will reinstantiate the form and give it a less boring context and
-request than ``None``.
+To hook up the AJAX handler as a public URL, we use a pluggable traverser
+traversal plugin. Let's first instantiate a pluggable traverser providing our
+form as the context.
- >>> class SomeContext(object):
- ... def __repr__(self):
- ... return '<%s>' % self.__class__.__name__
-
- >>> request = TestRequest(form={'message': u'hello'})
- >>> ping = PingForm(SomeContext(), request)
- >>> ping.update()
-
-Now we will instantiate a pluggable traverser providing our form as
-the context.
-
>>> from z3c.traverser.browser import PluggableBrowserTraverser
>>> traverser = PluggableBrowserTraverser(ping, request)
>>> traverser.publishTraverse(request, 'pingBack')()
@@ -99,10 +81,9 @@
...
NotFound: Object: <PingForm object at ...>, name: 'pingBack'
-We have not yet registered an plugin for the pluggable traverser.
-We will register the ``AJAXRequestTraverserPlugin`` which will only
-traverse to objects stored in the ``ajaxRequestHandlers`` selection
-manager.
+We have not yet registered a plugin for the pluggable traverser. We will
+register the ``AJAXRequestTraverserPlugin`` which will only traverse to
+objects stored in the ``ajaxRequestHandlers`` selection manager.
>>> import zope.component
>>> from z3c.traverser.interfaces import ITraverserPlugin
@@ -114,20 +95,28 @@
Now we will try traversing to our handler again.
- >>> traverser.publishTraverse(request, 'pingBack')()
- u'from <SomeContext>: hello'
+ >>> print traverser.publishTraverse(request, 'pingBack')()
+ from <PingForm ...>: Nothing to ping back.
-NOTE: The pluggable traverser itself can be registered in a number of
-ways. But the best way is to register it as a view for the from in
-question. Since forms generally inherit from the
-z3c.form.form.BaseForm object, which itself inherits from BrowserPage,
-most forms will already have a publishTraverse method which will
-override any attempt to adapt to a diferent traverser. But if you
-provide the pluggable traverser as a view on the form, then using the
-@@ symbols to force a view lookup rather than a publishTraverse call
-will bypass BrowserPage's publishTraverse method. In ZCML, the
-pluggable traverser gets registered as a named adatper like so:
+When providing a message, then the pin back is less silly:
+ >>> request = TestRequest(form={'message': 'hello'})
+ >>> ping = PingForm(None, request)
+ >>> traverser = PluggableBrowserTraverser(ping, request)
+ >>> print traverser.publishTraverse(request, 'pingBack')()
+ from <PingForm ...>: hello
+
+
+NOTE: The pluggable traverser itself can be registered in a number of ways.
+But the best way is to register it as a view for the from in question. Since
+forms generally inherit from the ``z3c.form.form.BaseForm`` object, which
+itself inherits from BrowserPage, most forms will already have a
+publishTraverse method which will override any attempt to adapt to a diferent
+traverser. But if you provide the pluggable traverser as a view on the form,
+then using the @@ symbols to force a view lookup rather than a publishTraverse
+call will bypass BrowserPage's publishTraverse method. In ZCML, the pluggable
+traverser gets registered as a named adatper like so:
+
<adapter
trusted="True"
for=".interfaces.IFormTraverser
More information about the Checkins
mailing list