Dieter Maurer wrote:
As soon as you index the content, you will be interested
to distinguish between a modification in the primary
content and some meta data (as it has big repercussions
on the speed of the reindexing).


I agree, but perhaps we can find a compromise that fits
all needs. I propose to use pluggable modification utilities.

The container related events are already fired by only two
functions (setitem and uncontained). If we introduce a third
one for object modification (e.g. update or modify and an
additional annotate if needed), we can replace these
functions with callable utilities, which are responsible for
performing the changes and  firing the events.
It's then up these components which events are
actually used.

Direct calls of ObjectModifiedEvents can then be marked as
deprecated to ensure that all applications will use
these pluggable modifiers.

Regards,
Uwe

The following code should make clear what I mean:

#! python

import doctest, unittest, zope
from zope.app import zapi
from zope.interface import Interface, implements
from zope.app.event.objectevent import ObjectModifiedEvent
from zope.app.event.interfaces import IObjectModifiedEvent

class IPluggableModifier(Interface) :
    """
    A pluggable modifier can be used to replace the
    existing modification mechanism in  Zope.
    It should be used in all places where other systems
    are notified about changes.
        
    The implementation must ensure that
    at least one IObjectModifiedEvent is fired
    if an object's state has been changed.
    """
       
    def __call__(self, obj, interface, **kw) :
        """
        Performs the update and fires an IObjectModifiedEvent.
        
        It's up to the plugin which specialization of
        IObjectModifiedEvent is actually used.
        
        Returns the modified object or None if the object
        could not be modified.
        """
        
class DefaultModifier(object) :
    """
    Implements a default behavior that mimics Zope3's current
    modification event handling.
        
    Setup :
        
        >>> from zope.component import provideUtility
        >>> provideUtility(DefaultModifier(), IPluggableModifier)
        >>> events = []
        >>> zope.event.subscribers.append(events.append)
        >>> class Sample(object) : pass
        
    Usage :
    
        >>> modify = zapi.getUtility(IPluggableModifier)
        >>> sample = Sample()
        >>> result = modify(sample, None, title='Test', description='Example')
        >>> result == sample
        True
        >>> IObjectModifiedEvent.providedBy(events[0])
        True
        >>> sample.title
        'Test'
        >>> sample.description
        'Example'
        
        
    """
    implements(IPluggableModifier)
    
    def __call__(self, obj, interface, **kw) :
        """ Modifies an object and fires an IObjectModifiedEvent. """
        
        if interface is not None :
            obj = interface(obj)
        for key, value in kw.items() :
            setattr(obj, key, value)
        zope.event.notify(ObjectModifiedEvent(obj))
        return obj


class ValueChangedEvent(object) :
    """
    A modification event that keeps additional information about
    the used interface, the old and new values.
    """
    
    def __init__(self, object, interface, key, old_value, new_value) :
        self.object = object
        self.interface = interface
        self.key = key
        self.old_value = old_value
        self.new_value = new_value
    
class BetterModifier(object) :
    """
    Implements a modification behavior that fires more
    informative events for more efficient versioning
    and reindexing.

    Setup :
        
        >>> from zope.component import provideUtility
        >>> provideUtility(BetterModifier(), IPluggableModifier)
        >>> events = []
        >>> zope.event.subscribers.append(events.append)
        >>> class Sample(object) : pass
        
    Usage :
    
        >>> modify = zapi.getUtility(IPluggableModifier)
        >>> sample = Sample()
        >>> result = modify(sample, None, title='Test', description='Example')
        >>> result == sample
        True
        >>> sample.title
        'Test'
        >>> sample.description
        'Example'
        >>> for x in events : print x.__class__.__name__
        ValueChangedEvent
        ValueChangedEvent
        ObjectModifiedEvent

    """
    implements(IPluggableModifier)
        
    undefined = object()
    
    def __call__(self, obj, interface, **kw) :
        """ Modifies an object and fires an IObjectModifiedEvent. """
        
        if interface is not None :
            obj = interface(obj)
        for key, value in kw.items() :
            old = getattr(obj, key, self.undefined)
            if old != value :
                setattr(obj, key, value)
                event = ValueChangedEvent(obj, interface, key, old, value)
                zope.event.notify(event)
        zope.event.notify(ObjectModifiedEvent(obj))
        return obj
    

from doctest import DocTestSuite

def test_suite():
    return unittest.TestSuite((
        DocTestSuite(),
        ))

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')
_______________________________________________
Zope3-dev mailing list
Zope3-dev@zope.org
Unsub: http://mail.zope.org/mailman/options/zope3-dev/archive%40mail-archive.com

Reply via email to