[Checkins] SVN: z3c.formjs/trunk/ reinforced ajax.txt tests and provided more explanation about how the traverser works.

Paul Carduner paulcarduner at gmail.com
Sun Jul 15 03:55:00 EDT 2007


Log message for revision 77989:
  reinforced ajax.txt tests and provided more explanation about how the traverser works.

Changed:
  U   z3c.formjs/trunk/TODO.txt
  U   z3c.formjs/trunk/src/z3c/formjs/ajax.py
  U   z3c.formjs/trunk/src/z3c/formjs/ajax.txt

-=-
Modified: z3c.formjs/trunk/TODO.txt
===================================================================
--- z3c.formjs/trunk/TODO.txt	2007-07-15 07:45:12 UTC (rev 77988)
+++ z3c.formjs/trunk/TODO.txt	2007-07-15 07:54:59 UTC (rev 77989)
@@ -2,6 +2,9 @@
 TODO
 ====
 
+ - modify validator code to use ajax.handler decorator and the @@ajax
+   view for communication.
+
  - A way to write a javascript function in python such that you can
    render a call to it from python
 
@@ -15,4 +18,4 @@
     'z3c_formjs_foo()'
 
  - client side js validators for simple fields.  (maybe we can use an
-   existing library?)
\ No newline at end of file
+   existing library?)

Modified: z3c.formjs/trunk/src/z3c/formjs/ajax.py
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/ajax.py	2007-07-15 07:45:12 UTC (rev 77988)
+++ z3c.formjs/trunk/src/z3c/formjs/ajax.py	2007-07-15 07:54:59 UTC (rev 77989)
@@ -35,6 +35,8 @@
         self._data_values.append(handler)
         self._data[name] = handler
 
+    def __repr__(self):
+        return "<%s %r>" % (self.__class__.__name__, self.keys())
 
 class AJAXRequestHandler(object):
     zope.interface.implements(interfaces.IAJAXRequestHandler,
@@ -46,12 +48,16 @@
 class AJAXHandler(BrowserPage):
     zope.interface.implements(interfaces.IAJAXHandler)
 
+    context = None
+
     def __init__(self, func):
         self.func = func
 
     def __call__(self):
         return self.func(self.context)
 
+    def __repr__(self):
+        return "<%s %r>" % (self.__class__.__name__, self.func.__name__)
 
 def handler(func):
     """A decorator for defining an AJAX request handler."""
@@ -78,5 +84,4 @@
             raise NotFound(self.context, name, request)
 
         handler.context = self.context
-        handler.request = self.request
-        return handler#(self.context, request)
+        return handler

Modified: z3c.formjs/trunk/src/z3c/formjs/ajax.txt
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/ajax.txt	2007-07-15 07:45:12 UTC (rev 77988)
+++ z3c.formjs/trunk/src/z3c/formjs/ajax.txt	2007-07-15 07:54:59 UTC (rev 77989)
@@ -34,25 +34,110 @@
   ...     
   ...     @ajax.handler
   ...     def pingBack(self):
-  ...         return request.get('message', 'Nothing to ping back.')
+  ...         message = self.request.get('message', 'Nothing to ping back.')
+  ...         return "from %r: %s" % (self.context, message)
 
-Now we can call the pingBack method from a url using a pluggable traverser
-that we register.
+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.
 
   >>> from z3c.form.testing import TestRequest
-  >>> import zope.component
+  >>> request = TestRequest()
+  >>> ping = PingForm(None, request)
+  >>> ping.ajaxRequestHandlers
+  <AJAXHandlers ['pingBack']>
+  >>> ping.pingBack
+  <AJAXHandler 'pingBack'>
+  >>> 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.
+
+  >>> ping.pingBack()
+  Traceback (most recent call last):
+  ...
+  AttributeError: 'NoneType' object has no attribute 'request'
+
+So we need to set the context for the handler before we call it.
+
+  >>> ping.pingBack.context = ping
+  >>> ping.pingBack()
+  'from None: Nothing to ping back.'
+
+Since the function is now a publishable object, it provides the
+``IBrowserPublisher`` interface
+
+  >>> from zope.publisher.interfaces.browser import IBrowserPublisher
+  >>> IBrowserPublisher.providedBy(ping.pingBack)
+  True
+
+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``.
+
+  >>> 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')()
+  Traceback (most recent call last):
+  ...
+  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.
+
+  >>> import zope.component
   >>> from z3c.traverser.interfaces import ITraverserPlugin
   >>> from zope.publisher.interfaces.browser import IBrowserRequest
   >>> zope.component.provideSubscriptionAdapter(
   ...     ajax.AJAXRequestTraverserPlugin,
   ...     (interfaces.IFormTraverser, IBrowserRequest),
   ...     provides=ITraverserPlugin)
-  >>> request = TestRequest(form={'message': u'hello'})
-  >>> ping = PingForm(None, request)
-  >>> ping.update()
-  >>> traverser = PluggableBrowserTraverser(ping, request)
+
+Now we will try traversing to our handler again.
+
   >>> traverser.publishTraverse(request, 'pingBack')()
-  u'hello'
+  u'from <SomeContext>: 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
+	   zope.publisher.interfaces.browser.IBrowserRequest"
+      provides="zope.publisher.interfaces.browser.IBrowserPublisher"
+      factory="z3c.traverser.browser.PluggableBrowserTraverser"
+      permission="zope.Public"
+      name="ajax"
+      />
+
+This makes the plggable traverser available via the @@ajax "view".
+In a url, an ajax request handler would be called via the url:
+http://host/path/to/context/@@form.html/@@ajax/pingBack



More information about the Checkins mailing list