<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<style type="text/css">body{margin-left:10px;margin-right:10px;margin-top:0px;margin-bottom:0px;}</style>
</head>
<body marginleft="10" marginright="10" margintop="0" marginbottom="0">
<font face="Geneva" size="+0" color="#000000" style="font-family:Geneva;font-size:10pt;color:#000000;">Dieter Maurer wrote:<br>
</font><span style="background-color:#d0d0d0"><font face="Geneva" size="+0" color="#000000" style="font-family:Geneva;font-size:10pt;color:#000000;">As soon as you index the content, you will be interested<br>
to distinguish between a modification in the primary<br>
content and some meta data (as it has big repercussions<br>
on the speed of the reindexing).</font></span><font face="Geneva" size="+0" color="#000000" style="font-family:Geneva;font-size:10pt;color:#000000;"><br>
<br>
I agree, but perhaps we can find a compromise that fits <br>
all needs. I propose to use pluggable modification utilities.<br>
<br>
The container related events are already fired by only two <br>
functions (setitem and uncontained). If we introduce a third <br>
one for object modification (e.g. update or modify and an <br>
additional annotate if needed), we can replace these <br>
functions with callable utilities, which are responsible for <br>
performing the changes and firing the events.<br>
It's then up these components which events are<br>
actually used.<br>
<br>
Direct calls of ObjectModifiedEvents can then be marked as<br>
deprecated to ensure that all applications will use<br>
these pluggable modifiers.<br>
<br>
Regards,<br>
Uwe<br>
<br>
The following code should make clear what I mean:<br>
<br>
#! python<br>
<br>
import doctest, unittest, zope<br>
from zope.app import zapi<br>
from zope.interface import Interface, implements<br>
from zope.app.event.objectevent import ObjectModifiedEvent<br>
from zope.app.event.interfaces import IObjectModifiedEvent<br>
<br>
class IPluggableModifier(Interface) :<br>
"""<br>
A pluggable modifier can be used to replace the<br>
existing modification mechanism in Zope. <br>
It should be used in all places where other systems <br>
are notified about changes.<br>
<br>
The implementation must ensure that<br>
at least one IObjectModifiedEvent is fired<br>
if an object's state has been changed.<br>
"""<br>
<br>
def __call__(self, obj, interface, **kw) :<br>
"""<br>
Performs the update and fires an IObjectModifiedEvent.<br>
<br>
It's up to the plugin which specialization of<br>
IObjectModifiedEvent is actually used.<br>
<br>
Returns the modified object or None if the object<br>
could not be modified.<br>
"""<br>
<br>
class DefaultModifier(object) :<br>
"""<br>
Implements a default behavior that mimics Zope3's current<br>
modification event handling.<br>
<br>
Setup :<br>
<br>
>>> from zope.component import provideUtility<br>
>>> provideUtility(DefaultModifier(), IPluggableModifier)<br>
>>> events = []<br>
>>> zope.event.subscribers.append(events.append)<br>
>>> class Sample(object) : pass<br>
<br>
Usage :<br>
<br>
>>> modify = zapi.getUtility(IPluggableModifier)<br>
>>> sample = Sample()<br>
>>> result = modify(sample, None, title='Test', description='Example')<br>
>>> result == sample<br>
True<br>
>>> IObjectModifiedEvent.providedBy(events[0])<br>
True<br>
>>> sample.title<br>
'Test'<br>
>>> sample.description<br>
'Example'<br>
<br>
<br>
"""<br>
implements(IPluggableModifier)<br>
<br>
def __call__(self, obj, interface, **kw) :<br>
""" Modifies an object and fires an IObjectModifiedEvent. """<br>
<br>
if interface is not None :<br>
obj = interface(obj)<br>
for key, value in kw.items() :<br>
setattr(obj, key, value)<br>
zope.event.notify(ObjectModifiedEvent(obj))<br>
return obj<br>
<br>
<br>
class ValueChangedEvent(object) :<br>
"""<br>
A modification event that keeps additional information about<br>
the used interface, the old and new values.<br>
"""<br>
<br>
def __init__(self, object, interface, key, old_value, new_value) :<br>
self.object = object<br>
self.interface = interface<br>
self.key = key<br>
self.old_value = old_value<br>
self.new_value = new_value<br>
<br>
class BetterModifier(object) :<br>
"""<br>
Implements a modification behavior that fires more<br>
informative events for more efficient versioning<br>
and reindexing.<br>
<br>
Setup :<br>
<br>
>>> from zope.component import provideUtility<br>
>>> provideUtility(BetterModifier(), IPluggableModifier)<br>
>>> events = []<br>
>>> zope.event.subscribers.append(events.append)<br>
>>> class Sample(object) : pass<br>
<br>
Usage :<br>
<br>
>>> modify = zapi.getUtility(IPluggableModifier)<br>
>>> sample = Sample()<br>
>>> result = modify(sample, None, title='Test', description='Example')<br>
>>> result == sample<br>
True<br>
>>> sample.title<br>
'Test'<br>
>>> sample.description<br>
'Example'<br>
>>> for x in events : print x.__class__.__name__<br>
ValueChangedEvent<br>
ValueChangedEvent<br>
ObjectModifiedEvent<br>
<br>
"""<br>
implements(IPluggableModifier)<br>
<br>
undefined = object()<br>
<br>
def __call__(self, obj, interface, **kw) :<br>
""" Modifies an object and fires an IObjectModifiedEvent. """<br>
<br>
if interface is not None :<br>
obj = interface(obj)<br>
for key, value in kw.items() :<br>
old = getattr(obj, key, self.undefined)<br>
if old != value :<br>
setattr(obj, key, value)<br>
event = ValueChangedEvent(obj, interface, key, old, value)<br>
zope.event.notify(event)<br>
zope.event.notify(ObjectModifiedEvent(obj))<br>
return obj<br>
<br>
<br>
from doctest import DocTestSuite<br>
<br>
def test_suite():<br>
return unittest.TestSuite((<br>
DocTestSuite(),<br>
))<br>
<br>
if __name__ == '__main__':<br>
unittest.main(defaultTest='test_suite')<br>
</font>
</body></html>