[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