When a move event happens for an object, it's important to notify
subobjects as well.

We do this based on locations.
  
  >>> from zope.interface import implements
  >>> from zope.location.interfaces import ILocation, ISublocations
  >>> from cromlech.container.contained import dispatchToSublocations
  >>> from zope.lifecycleevent import ObjectRemovedEvent

Suppose, for example, that we define some location objects.

  >>> class L(object):
  ...     implements(ILocation)
  ...     def __init__(self, name):
  ...         self.__name__ = name
  ...         self.__parent__ = None
  ...     def __repr__(self):
  ...         return '%s(%s)' % (
  ...                 self.__class__.__name__, str(self.__name__))

  >>> class C(L):
  ...     implements(ISublocations)
  ...     def __init__(self, name, *subs):
  ...         L.__init__(self, name)
  ...         self.subs = subs
  ...         for sub in subs:
  ...             sub.__parent__ = self
  ...
  ...     def sublocations(self):
  ...         return self.subs

  >>> c = C(1,
  ...       C(11,
  ...         L(111),
  ...         L(112),
  ...         ),
  ...       C(12,
  ...         L(121),
  ...         L(122),
  ...         L(123),
  ...         L(124),
  ...         ),
  ...       L(13),
  ...       )

Now, if we call the dispatcher, it should call event handlers
for all of the objects.

Lets create an event handler that records the objects it sees:

  >>> seen = []
  >>> def handler(ob, event):
  ...     seen.append((ob, event.object))

Note that we record the the object the handler is called on as
well as the event object:

Now we'll register it:

  >>> from zope import component
  >>> from zope.lifecycleevent.interfaces import IObjectMovedEvent
  >>> component.provideHandler(handler, [None, IObjectMovedEvent])

We also register our dispatcher:

  >>> component.provideHandler(dispatchToSublocations,
  ...   [None, IObjectMovedEvent])

We can then call the dispatcher for the root object:

  >>> event = ObjectRemovedEvent(c)
  >>> dispatchToSublocations(c, event)

Now, we should have seen all of the subobjects:

  >>> seenreprs = map(repr, seen)
  >>> seenreprs.sort()
  >>> seenreprs
  ['(C(11), C(1))', '(C(12), C(1))', '(L(111), C(1))', '(L(112), C(1))', '(L(121), C(1))', '(L(122), C(1))', '(L(123), C(1))', '(L(124), C(1))', '(L(13), C(1))']

We see that we get entries for each of the subobjects and
that,for each entry, the event object is top object.

This suggests that location event handlers need to be aware that
the objects they are called on and the event objects could be
different.

  >>> component.getSiteManager().unregisterHandler(
  ...                              handler, [None, IObjectMovedEvent])
  True
