[Checkins] SVN: z3c.formjs/trunk/src/z3c/formjs/ Rework pretty much how the entire client event thing is done. It is still very rough and broken. I cannot figure out how to get the subscribers correctly.

Paul Carduner paulcarduner at gmail.com
Fri Aug 17 20:14:13 EDT 2007


Log message for revision 78913:
  Rework pretty much how the entire client event thing is done.  It is still very rough and broken.  I cannot figure out how to get the subscribers correctly.

Changed:
  U   z3c.formjs/trunk/src/z3c/formjs/interfaces.py
  U   z3c.formjs/trunk/src/z3c/formjs/jsclientevent.py
  U   z3c.formjs/trunk/src/z3c/formjs/jsclientevent.txt

-=-
Modified: z3c.formjs/trunk/src/z3c/formjs/interfaces.py
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/interfaces.py	2007-08-17 21:03:54 UTC (rev 78912)
+++ z3c.formjs/trunk/src/z3c/formjs/interfaces.py	2007-08-18 00:14:12 UTC (rev 78913)
@@ -307,3 +307,33 @@
 class IFormTraverser(zope.interface.Interface):
     """Marker interface for forms that can be traversed by the @@ajax
     view."""
+
+
+
+# -----[ Client Side Event System ]---------------------------------------
+
+class IClientEventHandlers(zope.interface.Interface):
+    """A collection of client side event handlers for server side events."""
+
+    def addHandler(required, handler):
+        """Add a new handler for a the given required interfaces."""
+
+    def getHandler(event):
+        """Get the handler for the given server side event."""
+
+    def copy():
+        """Copy this object and return the copy."""
+
+    def __add__(other):
+        """Add another handlers object.
+
+        During the process a copy of the current handlers object should be
+        created and the other one is added to the copy. The return value is
+        the copy.
+        """
+
+class IClientEventHandler(zope.interface.Interface):
+    """A handler managed by the client event handlers."""
+
+    def __call__(self, form, event):
+        """Execute the handler."""

Modified: z3c.formjs/trunk/src/z3c/formjs/jsclientevent.py
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/jsclientevent.py	2007-08-17 21:03:54 UTC (rev 78912)
+++ z3c.formjs/trunk/src/z3c/formjs/jsclientevent.py	2007-08-18 00:14:12 UTC (rev 78913)
@@ -19,27 +19,78 @@
 import sys
 import zope.component
 import zope.interface
+from zope.interface import adapter
+
 from zope.security.management import getInteraction
 from zope.publisher.interfaces.browser import IBrowserRequest
 
 from z3c.formjs import interfaces, jsfunction
 
-def listener(eventType):
-    """A decorator for defining a javascript function that is a listener."""
-    namespace = "%s_%s" % (eventType.__module__.replace(".","_"), eventType.__name__)
-##     def createFunction(func):
-##         import pdb; pdb.set_trace()
-##         frame = sys._getframe(1)
-##         f_locals = frame.f_locals
-##         funcs = f_locals.setdefault('jsFunctions', jsfunction.JSFunctions())
-##         jsFunction = jsfunction.JSFunction(namespace, func)
-##         return funcs.add(jsFunction, namespace)
+CLIENT_EVENT_REQUEST_KEY = "z3c.formjs.jsclientevent.caughtEvents"
 
-    zope.component.provideHandler(serverToClientEventLoader, (eventType,))
-    return jsfunction.function(namespace) #createFunction
 
-CLIENT_EVENT_REQUEST_KEY = "z3c.formjs.jsclientevent.caughtEvents"
+class ClientEventHandlers(object):
+    """Client Handlers for server side events."""
 
+    def __init__(self):
+        self._registry = adapter.AdapterRegistry()
+        self._handlers = ()
+
+    def addHandler(self, required, handler):
+        """See interfaces.IClientEventHandlers"""
+        # Register the handler
+        self._registry.subscribe(
+            required, interfaces.IClientEventHandler, handler)
+        self._handlers += ((required, handler),)
+
+    def getHandlers(self, event):
+        """See interfaces.IClientEventHandlers"""
+        return self._registry.subscribers((event.object, event), None)
+
+    def copy(self):
+        """See interfaces.IClientEventHandlers"""
+        handlers = Handlers()
+        for eventSpec, handler in self._handlers:
+            handlers.addHandler(eventSpec, handler)
+        return handlers
+
+    def __add__(self, other):
+        """See interfaces.IClientEventHandlers"""
+        if not isinstance(other, ClientEventHandlers):
+            raise NotImplementedError
+        handlers = self.copy()
+        for required, handler in other._handlers:
+            handlers.addHandler(required, handler)
+        return handlers
+
+    def __repr__(self):
+        return '<Handlers %r>' %[handler for required, handler in self._handlers]
+
+
+class ClientEventHandler(object):
+    zope.interface.implements(interfaces.IClientEventHandler)
+
+    def __init__(self, required, func):
+        self.required = required
+        self.func = func
+
+    def __call__(self, form, event):
+        return self.func(form, event)
+
+    def __repr__(self):
+        return '<%s for %r>' % (self.__class__.__name__, self.required)
+
+
+def listener(required):
+    """A decorator for defining a javascript function that is a listener."""
+    def createListener(func):
+        frame = sys._getframe(1)
+        f_locals = frame.f_locals
+        handlers = f_locals.setdefault('jsClientListeners', ClientEventHandlers())
+        jsListener = ClientEventHandler(required, func)
+        return handlers.addHandler(required, jsListener)
+    return createListener
+
 def serverToClientEventLoader(event):
     """Event handler that listens for server side events
     and stores the event in the request to be picked up by
@@ -69,4 +120,13 @@
 
     @property
     def eventCalls(self):
-        return self.request.annotations.get(CLIENT_EVENT_REQUEST_KEY, [])
+        events = self.request.annotations.get(CLIENT_EVENT_REQUEST_KEY, [])
+        listeners = getattr(self, 'jsClientListeners', None)
+        if listeners is None:
+            return []
+        result = []
+        for event in events:
+            import pdb; pdb.set_trace()
+            if listeners.getHandlers(event):
+                result.append(event)
+        return result

Modified: z3c.formjs/trunk/src/z3c/formjs/jsclientevent.txt
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/jsclientevent.txt	2007-08-17 21:03:54 UTC (rev 78912)
+++ z3c.formjs/trunk/src/z3c/formjs/jsclientevent.txt	2007-08-18 00:14:12 UTC (rev 78913)
@@ -18,15 +18,12 @@
 defined on the client side.
 
 
-Client Side Listener
+Client Side Handler
 --------------------
 
-The client side event listener is just a JavaScript function that gets
-called when the event occurs.  In order for the server to know which
-JavaScript functions to call, we must define the JavaScript function
-itself on the server, in python, so it can be referenced by the
-server.  This works a lot like the jsfunction module with slight
-modifications.
+The client side event handler is just a snippet of JavaScript that
+gets executed when the event occurs.  The server injects this
+javascript onto the page via an ajax response.
 
 Let's create a simple view class with an event listener for the
 IObjectModifiedEvent
@@ -34,31 +31,23 @@
   >>> from zope.lifecycleevent.interfaces import IObjectModifiedEvent
   >>> class View(object):
   ...
-  ...     @jsclientevent.listener(IObjectModifiedEvent)
-  ...     def modifiedListener(self, data):
-  ...         return 'alert("object modified: " + data);'
+  ...     @jsclientevent.listener((IObjectModifiedEvent,))
+  ...     def modifiedListener(self, event):
+  ...         return 'alert("object modified: %s");' % event.object
 
-The argument passed to the ``listener`` decorator is the interface for
-the event that this function will be listening for.  This is the only
-thing that makes this decorator different from the jsfunction.function
-decorator.  Specifically, the jsclientevent.listener decorator sets
-the namespace of the function based on the type of event passed to it.
+The argument passed to the ``listener`` decorator is a list (or tuple)
+of the required interfaces for the event handler.  This is just like
+the set of interfaces one would pass to the
+zope.component.provideHandler function.
 
-All these event listeners are collected in the ``jsFunctions``
-attribute because that is after all what they are.
+The decorator also registers this client side event handler with a
+local component registry that is accessed through the
+``jsClientListeners`` attribute, which is now available on the view class itself.
 
-  >>> View.jsFunctions
-  <JSFunctions
-     {'zope_lifecycleevent_interfaces_IObjectModifiedEvent': [<JSFunction modifiedListener>]}>
+  >>> View.jsClientListeners
+  <Handlers
+     [<ClientEventHandler for (<InterfaceClass zope.lifecycleevent.interfaces.IObjectModifiedEvent>,)>]>
 
-  >>> print View.jsFunctions.render()
-  var zope_lifecycleevent_interfaces_IObjectModifiedEvent = {
-    modifiedListener: function(data) {
-      alert("object modified: " + data);
-    }
-  }
-
-
 Server Side Listeners
 ---------------------
 
@@ -68,8 +57,9 @@
 First we need to register the subscriber.
 
   >>> import zope.component
+  >>> from zope.component.interfaces import IObjectEvent
   >>> zope.component.provideHandler(jsclientevent.serverToClientEventLoader,
-  ...                               (IObjectModifiedEvent,))
+  ...                               (IObjectEvent,))
 
   >>> from zope.event import notify
   >>> from zope.lifecycleevent import ObjectModifiedEvent
@@ -84,7 +74,6 @@
   >>> from z3c.formjs import testing
   >>> testing.setupRenderers()
 
-
 Create a content component for an "article"
 
   >>> import zope.interface
@@ -107,7 +96,7 @@
   ...                       form.EditForm):
   ...     fields = field.Fields(IArticle)
   ...
-  ...     @jsclientevent.listener(IObjectModifiedEvent)
+  ...     @jsclientevent.listener((IObjectModifiedEvent,))
   ...     def alertModifiedEvent(self, data):
   ...         return ('alert("You modified the object.\ndata: "'
   ...                 ' + data + "\nPlease reload the page."')
@@ -135,4 +124,21 @@
 More importantly, now our form knows about this event.
 
   >>> form.eventCalls
-  [<zope.app.event.objectevent.ObjectModifiedEvent object at ...>]
\ No newline at end of file
+  [<zope.app.event.objectevent.ObjectModifiedEvent object at ...>]
+
+But what happens if we throw events for which there are not handlers?
+They should not show up in the ``eventCalls`` attribute.  First of
+all, events that are not sent out when an interaction is not taking
+place are not picked up at all -- not event in the request annotation.
+Since we have not ended the last interaction though, any events we
+we notify will get picked up.
+
+  >>> from zope.component.interfaces import ObjectEvent
+  >>> notify(ObjectEvent('foo'))
+  >>> request.annotations[jsclientevent.CLIENT_EVENT_REQUEST_KEY]
+  [<zope.app.event.objectevent.ObjectModifiedEvent object at ...>,
+   <zope.app.event.objectevent.ObjectEvent object at ...>]
+
+But this still does not show up in the eventCalls.
+
+  >>> form.eventCalls
\ No newline at end of file



More information about the Checkins mailing list