[Zope3-checkins] SVN: Zope3/branches/srichter-blow-services/src/zope/app/component/ As promised on IRC yesterday, here are the registration framework tests.

Stephan Richter srichter at cosmos.phy.tufts.edu
Thu Dec 23 17:44:57 EST 2004


Log message for revision 28700:
  As promised on IRC yesterday, here are the registration framework tests. 
  Now its time to get the local component architecture running again.
  
  

Changed:
  U   Zope3/branches/srichter-blow-services/src/zope/app/component/hooks.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/component/interfaces/registration.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/component/registration.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/component/registration.txt
  U   Zope3/branches/srichter-blow-services/src/zope/app/component/statusproperty.txt
  U   Zope3/branches/srichter-blow-services/src/zope/app/component/tests/test_registration.py

-=-
Modified: Zope3/branches/srichter-blow-services/src/zope/app/component/hooks.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/hooks.py	2004-12-23 22:43:11 UTC (rev 28699)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/hooks.py	2004-12-23 22:44:57 UTC (rev 28700)
@@ -41,7 +41,7 @@
 
 class SiteInfo(zope.thread.local):
     site = None
-    #services = serviceManager
+    sm = zope.component.getGlobalSiteManager()
 
     def adapter_hook(self):
         services = self.services
@@ -56,7 +56,7 @@
 
 def setSite(site=None):
     if site is None:
-        services = serviceManager
+        sm = zope.component.getGlobalSiteManager()
     else:
 
         # We remove the security proxy because there's no way for
@@ -67,10 +67,10 @@
         # they can't be proxied.  Well, except maybe for performance.
         
         site = removeSecurityProxy(site)
-        services = site.getSiteManager()
+        sm = site.getSiteManager()
 
     siteinfo.site = site
-    siteinfo.services = services
+    siteinfo.services = sm
     try:
         del siteinfo.adapter_hook
     except AttributeError:

Modified: Zope3/branches/srichter-blow-services/src/zope/app/component/interfaces/registration.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/interfaces/registration.py	2004-12-23 22:43:11 UTC (rev 28699)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/interfaces/registration.py	2004-12-23 22:44:57 UTC (rev 28700)
@@ -79,30 +79,33 @@
 
 
 class IComponentRegistration(IRegistration):
-    """Registration object that uses a component path and a permission."""
+    """Registration object that uses a component.
 
-    permission = Choice(
-        title=_("The permission needed to use the component"),
-        vocabulary="Permissions",
-        required=False,
-        )
-
+    An interface can optionally be specified that describes the interface the
+    component provides for the registry.
+    
+    The interface will be used to produce a proxy for the component, if
+    the permission is also specified.
+    """
     component = Component(
         title=_("Registration Component"),
         description=_("The component the registration is for."),
         required=True)
 
-    def getInterface(self):
-        """Return the interface the component provides through this
-        registration.
+    interface = Field(
+        title=_("Component Interface"),
+        description=_("The interface the component provides through this "
+                      "registration."),
+        required=False,
+        default=None)
 
-        If no interface was specified, return `None`.
+    permission = Choice(
+        title=_("The permission needed to use the component"),
+        vocabulary="Permissions",
+        required=False
+        )
 
-        The interface will be used to produce a proxy for the component, if
-        the `permission` is specified.
-        """
 
-
 class IRegistry(zope.component.interfaces.IRegistry):
     """A component that can be configured using a registration manager."""
 

Modified: Zope3/branches/srichter-blow-services/src/zope/app/component/registration.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/registration.py	2004-12-23 22:43:11 UTC (rev 28699)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/registration.py	2004-12-23 22:44:57 UTC (rev 28700)
@@ -19,8 +19,8 @@
 
 import zope.event
 from zope.interface import implements
-from zope.security.checker import CheckerPublic
-from zope.security.proxy import removeSecurityProxy
+from zope.security.checker import InterfaceChecker, CheckerPublic
+from zope.security.proxy import Proxy, removeSecurityProxy
 
 from zope.app import zapi
 from zope.app.container.btree import BTreeContainer
@@ -66,9 +66,12 @@
         if value == interfaces.ActiveStatus:
             if not registry.registered(registration):
                 registry.register(registration)
+                zope.event.notify(RegistrationActivatedEvent(registration))
+
         elif value == interfaces.InactiveStatus:
             if registry.registered(registration):
                 registry.unregister(registration)
+                zope.event.notify(RegistrationDeactivatedEvent(registration))
         else:
             raise ValueError, value
 
@@ -101,8 +104,8 @@
         self.permission = permission
 
     def _getComponent(self):
-        if self.permission and self.getInterface():
-            checker = InterfaceChecker(self.getInterface(), self.permission)
+        if self.permission and self.interface:
+            checker = InterfaceChecker(self.interface, self.permission)
             return Proxy(self._component, checker)
         return self._component
 
@@ -111,10 +114,11 @@
         # get back a proxied component anyways.
         self._component = removeSecurityProxy(component)
 
+    # See zope.app.component.interfaces.registration.IComponentRegistration
     component = property(_getComponent, _setComponent)
 
-    def getInterface(self):
-        return None
+    # See zope.app.component.interfaces.registration.IComponentRegistration
+    interface = None
 
 
 def SimpleRegistrationRemoveSubscriber(registration, event):
@@ -137,19 +141,19 @@
                               % objectpath)
 
 
-def ComponentRegistrationRemoveSubscriber(component_registration, event):
+def ComponentRegistrationRemoveSubscriber(componentRegistration, event):
     """Receive notification of remove event."""
-    component = component_registration.component
+    component = componentRegistration.component
     dependents = IDependable(component)
-    objectpath = zapi.getPath(component_registration)
+    objectpath = zapi.getPath(componentRegistration)
     dependents.removeDependent(objectpath)
 
 
-def ComponentRegistrationAddSubscriber(component_registration, event):
+def ComponentRegistrationAddSubscriber(componentRegistration, event):
     """Receive notification of add event."""
-    component = component_registration.component
+    component = componentRegistration.component
     dependents = IDependable(component)
-    objectpath = zapi.getPath(component_registration)
+    objectpath = zapi.getPath(componentRegistration)
     dependents.addDependent(objectpath)
 
 
@@ -176,8 +180,8 @@
 
     def registrations(self):
         rm = zapi.getParent(self.registerable).registrationManager
-        return [reg for reg in rm
-                if (IComponentRegistration.providedBy(reg) and
+        return [reg for reg in rm.values()
+                if (interfaces.IComponentRegistration.providedBy(reg) and
                     reg.component is self.registerable)]
 
 

Modified: Zope3/branches/srichter-blow-services/src/zope/app/component/registration.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/registration.txt	2004-12-23 22:43:11 UTC (rev 28699)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/registration.txt	2004-12-23 22:44:57 UTC (rev 28700)
@@ -248,21 +248,180 @@
 The Component Registration
 --------------------------
 
-provided interface
-permission
-Registered
+Until now we have only discussed the most primitive usage of the
+`ComponentRegistration`. Usually, a registry is not just interested in a
+component, but a set of methods which are specified by a particular
+interface. Thus the component registration supports the `interface`
+attribute. By default it is `None`:
 
+  >>> regFoo.interface is None
+  True
 
-Registration Events
--------------------
+We can now write another `IComponentRegistration` implementation that knows
+about the interface; in fact, it will pick the most specific one of the
+component:
 
-RegistrationActivatedEvent
-RegistrationDeactivatedEvent
+  >>> from zope.interface import providedBy
+  >>> class SomethingRegistration(Registration):
+  ...
+  ...     def interface(self):
+  ...         return list(providedBy(self._component))[0]
+  ...     interface = property(interface)
+  
+Next we create an interface and its implementation:
 
+  >>> class ISomething(zope.interface.Interface):
+  ...     pass
 
+  >>> class Something(Component):
+  ...     zope.interface.implements(ISomething)
+
+Creating a "something registration", we can see that the interface attribute
+is now available:
+
+  >>> something = Something('Something')
+  >>> reg = SomethingRegistration(something)
+  >>> reg.interface
+  <InterfaceClass __builtin__.ISomething>
+
+But hold on, we are not done yet! The component registration also supports a
+`permission` attribute. When set and an interface is available, the component
+will always be proxied using an interface checker for the specified
+permission. By default the permission is `None`:
+
+  >>> reg.permission is None
+  True
+
+Now we set a permission for the registration and the component should be
+proxies when returned:
+
+  >>> from zope.security.checker import CheckerPublic  
+  >>> reg.permission = CheckerPublic
+  >>> reg.component is something
+  False
+  >>> type(reg.component) 
+  <type 'zope.security._proxy._Proxy'>
+
+You can also, specify a permission in the constructor:
+
+  >>> regNone = SomethingRegistration(None, 'zope.Public')
+  >>> regNone.permission is CheckerPublic
+  True
+
+If the interface is not available, the permission is ignored and the bare
+component is returned:
+
+  >>> regSomething2 = Registration(something, 'zope.Public')
+  >>> regSomething2.permission is CheckerPublic
+  True
+  >>> regSomething2.component is something
+  True
+
+
+The `Registered` Adapter
+------------------------
+
+Registerable components are able to get a list of all their
+registrations. However, the adapter only works for components and
+registrations that are stored in the registerable container and registration
+manager, respectively. 
+
+  >>> from zope.app.component.registration import Registered
+  >>> registered = Registered(foo)
+  >>> registered.registrations() #doctest: +NORMALIZE_WHITESPACE
+  [<Registration for '<Component: 'Foo'>'>, 
+   <Registration for '<Component: 'Foo'>'>]
+
+If the registerable component is not stored in a registrable container, a
+type error is raised, since no parent can be found:
+
+  >>> registered = Registered(something)
+  >>> registered.registrations() #doctest: +NORMALIZE_WHITESPACE
+  Traceback (most recent call last):
+  ...
+  TypeError: ('Not enough context information to get parent', 
+              <Component: 'Something'>)
+
+
 Registrations and Dependencies
 ------------------------------
 
-ComponentRegistrationRemoveSubscriber
-ComponentRegistrationAddSubscriber
-RegisterableMoveSubscriber
\ No newline at end of file
+Registerable objects and registrations have a very close dependence. For
+example, it makes no sense to delete a registerable component and leave its
+registrations around. Instead, registrations always are required to be removed
+before the component. Thus, it is also not allowed to simply copy a
+registerable component to another registerable container:
+
+  >>> orig = RegisterableManager()
+  >>> new = RegisterableManager()
+  >>> comp = Component('comp')
+  
+  >>> from zope.app.container.contained import ObjectMovedEvent
+  >>> event = ObjectMovedEvent(comp, orig, 'comp', new, 'comp')
+  
+  >>> from zope.app.component.registration import RegisterableMoveSubscriber
+  >>> RegisterableMoveSubscriber(comp, event)
+  Traceback (most recent call last):
+  ...
+  DependencyError: Can't move a registered component from its container.
+  
+Hoever, renaming the component is no problem:
+  
+  >>> event = ObjectMovedEvent(comp, orig, 'comp', orig, 'comp-new')
+  >>> RegisterableMoveSubscriber(comp, event)
+
+Whenever a registration is created it is added as a dependence to the
+registerable component, which is removed once the registration is removed. Two
+subscribers handle the management of the dependemcies. 
+
+Since the default `IDependable` adapter uses annotations to store the
+dependents, our component has to provide `IAttrbuteAnnotatable`:
+ 
+  >>> from zope.app.annotation.interfaces import IAttributeAnnotatable
+  >>> from zope.interface import directlyProvides
+  >>> directlyProvides(comp, IAttributeAnnotatable)
+
+Make sure that we do not initially have any dependencies:
+
+  >>> from zope.app.dependable.interfaces import IDependable
+  >>> dependents = IDependable(comp)
+  >>> dependents.getPaths()
+  ()
+
+Since the path of the registration is used to store the dependency, we need to
+make sure that we have a containment root; so make the registration itself the
+root:
+
+  >>> reg = Registration(comp)
+  >>> from zope.app.traversing.interfaces import IContainmentRoot
+  >>> directlyProvides(reg, IContainmentRoot)
+
+The component registration add subscriber adds a dependent.
+
+  >>> from zope.app.container.contained import ObjectAddedEvent
+  >>> from zope.app.component.registration import RegistrationManager
+  >>> event = ObjectAddedEvent(reg, RegistrationManager(), 'reg1')
+
+  >>> from zope.app.component.registration import \
+  ...     ComponentRegistrationAddSubscriber
+  >>> ComponentRegistrationAddSubscriber(reg, event)
+
+  >>> dependents = IDependable(comp)
+  >>> dependents.getPaths()
+  (u'/',)
+
+We simply got a slash here, since the registration is a root object. Now we
+remove the dependency again by calling the component registration remove
+subscriber:
+
+  >>> from zope.app.container.contained import ObjectRemovedEvent
+  >>> event = ObjectRemovedEvent(reg, RegistrationManager(), 'reg1')
+
+  >>> from zope.app.component.registration import \
+  ...     ComponentRegistrationRemoveSubscriber
+  >>> ComponentRegistrationRemoveSubscriber(reg, event)
+
+  >>> dependents = IDependable(comp)
+  >>> dependents.getPaths()
+  ()
+

Modified: Zope3/branches/srichter-blow-services/src/zope/app/component/statusproperty.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/statusproperty.txt	2004-12-23 22:43:11 UTC (rev 28699)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/statusproperty.txt	2004-12-23 22:44:57 UTC (rev 28700)
@@ -81,3 +81,44 @@
   True
   >>> reg in registry.registrations
   False
+
+
+Registration Events
+-------------------
+
+When a registration is activated or deactivated, an
+`RegistrationActivatedEvent` or `RegistrationDeactivatedEvent` is created,
+respectively. Listening to these events can be useful for cases where you want
+to change the component based on registration status.
+
+To catch the events, we have to register a subscriber with the event
+framework:
+
+  >>> events = []
+  >>> def subscriber(event):
+  ...     global events
+  ...     events.append(event)
+
+  >>> import zope.event
+  >>> zope.event.subscribers.append(subscriber)
+
+Now we switch our registration to active:
+
+  >>> reg.status = ActiveStatus
+  >>> event = events.pop()
+  >>> event.__class__
+  <class 'zope.app.component.registration.RegistrationActivatedEvent'>
+  >>> event.object is reg
+  True
+
+and deactivate it again:
+
+  >>> reg.status = InactiveStatus
+  >>> events.pop().__class__
+  <class 'zope.app.component.registration.RegistrationDeactivatedEvent'>
+  >>> event.object is reg
+  True
+
+Now make sure that we remove the subscriber again:
+
+  >>> del zope.event.subscribers[zope.event.subscribers.index(subscriber)]

Modified: Zope3/branches/srichter-blow-services/src/zope/app/component/tests/test_registration.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/tests/test_registration.py	2004-12-23 22:43:11 UTC (rev 28699)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/tests/test_registration.py	2004-12-23 22:44:57 UTC (rev 28700)
@@ -18,13 +18,21 @@
 __docformat__ = "reStructuredText"
 import unittest
 
+import zope.component.testing as placelesssetup
 from zope.testing import doctest
+from zope.app.testing import setup
 
+def setUp(test):
+    placelesssetup.setUp(test)
+    setup.setUpAnnotations()
+    setup.setUpDependable()
+    setup.setUpTraversal()
 
 def test_suite():
     return unittest.TestSuite((
         doctest.DocFileSuite('../statusproperty.txt'),
-        doctest.DocFileSuite('../registration.txt'),
+        doctest.DocFileSuite('../registration.txt',
+                             setUp=setUp, tearDown=placelesssetup.tearDown),
         ))
 
 if __name__ == "__main__":



More information about the Zope3-Checkins mailing list