more on relations - now includes an IObjectRemoved event handler and more fine-grained permissions

git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@657 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2005-11-05 14:13:47 +00:00
parent c04f84ed8f
commit 3c8b67a378
4 changed files with 105 additions and 19 deletions

View file

@ -1,13 +1,19 @@
Yet Another Relations Engine...
===============================
Yet Another Relations (Reference) Engine...
===========================================
>>> from zope.app.testing.placelesssetup import setUp
>>> setUp()
Let's start with two classes and a few objects that we will connect using
relations:
relations (we derive the classes from Persistent, thus we will be able to
reference these objects via IntIds later; the __parent__ and __name__
attributes are also needed later when we send an IObjectRemovedEvent event):
>>> class Person(object):
... pass
>>> from persistent import Persistent
>>> class Person(Persistent):
... __name__= __parent__ = None
>>> class City(object):
>>> class City(Persistent):
... pass
>>> clark = Person()
@ -20,6 +26,7 @@ The relation we'll use tells us in which city a person lives; this is a dyadic
relation as it connects two objects. We also associate the relationship
with an interface as we will later use this interface for querying relations.
Dyadic Relations
~~~~~~~~~~~~~~~~
@ -65,6 +72,7 @@ It is also possible to remove a relation from the relation registry:
>>> nyRels[0].first == kirk
True
Triadic Relations
~~~~~~~~~~~~~~~~~
@ -97,14 +105,19 @@ all relations for clark's children:
>>> clarkChildren[0].third == kirk
True
Setting up and using a RelationsRegistry local utility
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We now do the same stuff as above with a real, catalog-based implementation of
the relations registry.
the relations registry. We also register the relations registry as a
utility for demonstration purposes (and to be able to use it later when
working with events).
>>> from cybertools.relation.registry import RelationsRegistry
>>> relations = RelationsRegistry()
>>> from cybertools.relation.interfaces import IRelationsRegistry
>>> from zope.app.testing import ztapi
>>> ztapi.provideUtility(IRelationsRegistry, RelationsRegistry())
In order to register relations the objects that are referenced have to be
registered with an IntIds (unique ids) utility, so we have to set up such
@ -112,7 +125,6 @@ an utility (using a stub/dummy implementation for testing purposes):
>>> from cybertools.relation.tests import IntIdsStub
>>> from zope.app.intid.interfaces import IIntIds
>>> from zope.app.testing import ztapi
>>> ztapi.provideUtility(IIntIds, IntIdsStub())
We also have to provide an adapter for the Relation objects that provides
@ -127,6 +139,9 @@ the attributes needed for indexing:
So we are ready again to register a set of relations with our new relations
registry and query it.
>>> from zope.app import zapi
>>> relations = zapi.getUtility(IRelationsRegistry)
>>> relations.register(LivesIn(clark, washington))
>>> relations.register(LivesIn(audrey, newyork))
>>> relations.register(LivesIn(kirk, newyork))
@ -170,3 +185,32 @@ It should work also for triadic relations:
>>> clarkChildren[0].third == kirk
True
Handling object removal
~~~~~~~~~~~~~~~~~~~~~~~
Often it is desirable to unregister a when one of the objects
involved in the relation is removed from its container. This can be
done by subscribing to IObjectRemovedEvent. The relation.registry module
provides a simple handler for this event.
>>> from zope.app.container.interfaces import IObjectRemovedEvent
>>> from zope.interface import Interface
>>> from cybertools.relation.registry import unregisterRelations
>>> ztapi.subscribe([Interface, IObjectRemovedEvent], None, unregisterRelations)
We simulate the removal of kirk by calling notify:
>>> from zope.app.container.contained import ObjectRemovedEvent
>>> from zope.event import notify
>>> len(relations.query(first=clark))
2
>>> notify(ObjectRemovedEvent(kirk))
Thus there should only remain one relation containing clark as first:
>>> len(relations.query(first=clark))
1

View file

@ -7,12 +7,16 @@
<localUtility class=".registry.RelationsRegistry">
<require
permission="zope.Public"
interface=".interfaces.IRelationsRegistry"
permission="zope.View"
interface=".interfaces.IRelationsRegistryQuery"
/>
<require
permission="zope.ManageContent"
interface=".interfaces.IRelationsRegistryUpdate"
/>
<require
interface="zope.app.catalog.interfaces.ICatalogQuery"
permission="zope.Public"
permission="zope.View"
/>
<require
interface="zope.app.catalog.interfaces.ICatalogEdit"
@ -29,8 +33,17 @@
for=".interfaces.IRelation"
provides=".registry.IIndexableRelation"
factory=".registry.IndexableRelationAdapter"
trusted="true"
/>
<subscriber
for="zope.interface.Interface
zope.app.container.interfaces.IObjectRemovedEvent"
handler=".registry.unregisterRelations"
/>
<!-- browser settings -->
<browser:tool
interface=".interfaces.IRelationsRegistry"
title="Relations Registry"

View file

@ -25,8 +25,9 @@ $Id$
from zope.interface import Interface, Attribute
class IRelationsRegistry(Interface):
""" Local utility for registering (cataloguing) and searching relations.
class IRelationsRegistryUpdate(Interface):
""" Interface for registering and unregistering relations with a
relations registry.
"""
def register(relation):
@ -37,6 +38,11 @@ class IRelationsRegistry(Interface):
""" Remove the relation given from this registry.
"""
class IRelationsRegistryQuery(Interface):
""" Interface for querying a relations registry.
"""
def query(**kw):
""" Return a list of relations that fulfill the criteria given.
@ -45,14 +51,15 @@ class IRelationsRegistry(Interface):
"""
class IRelationsRegistry(IRelationsRegistryUpdate, IRelationsRegistryQuery):
""" Local utility for registering (cataloguing) and searching relations.
"""
class IRelation(Interface):
""" Base class for relations.
"""
relationship = Attribute(
'A string specific for a kind of relation; e.g. the full module '
'path and class name of the class implementing a certain type of relation.')
class IDyadicRelation(IRelation):
""" Relation connecting two objects.

View file

@ -22,6 +22,7 @@ Implementation of the utilities needed for the relations package.
$Id$
"""
from persistent import Persistent
from zope.interface import Interface, Attribute, implements
from zope.app import zapi
from zope.app.catalog.catalog import Catalog
@ -120,9 +121,14 @@ class IndexableRelationAdapter(object):
def __getattr__(self, attr):
value = getattr(self.context, attr)
if isinstance(value, Persistent):
return _getUid(value)
else:
return value
# helper functions
def _getUid(ob):
return zapi.getUtility(IIntIds).getId(ob)
@ -132,3 +138,19 @@ def _getRelationship(relation):
def _getClassString(cls):
return cls.__module__ + '.' + cls.__name__
# event handler
def unregisterRelations(context, event):
""" Handles IObjectRemoved event: unregisters all relations for the
object that has been removed.
"""
relations = []
registry = zapi.getUtility(IRelationsRegistry)
for attr in ('first', 'second', 'third'):
relations = registry.query(**{attr: context})
for relation in relations:
registry.unregister(relation)
# to do: unregister relation also from the IntId utility
# (if appropriate).