[Zope3-checkins] CVS: Zope3/src/zope/app/utility - __init__.py:1.1 configure.zcml:1.1 interfaces.py:1.1 tests.py:1.1 utility.txt:1.1

Stephan Richter srichter at cosmos.phy.tufts.edu
Thu Mar 11 17:05:03 EST 2004


Update of /cvs-repository/Zope3/src/zope/app/utility
In directory cvs.zope.org:/tmp/cvs-serv4201/utility

Added Files:
	__init__.py configure.zcml interfaces.py tests.py utility.txt 
Log Message:
Moved local utility service implementation to zope.app.utility till Jim 
will provide the new one.

Note: I forgot this module in an earlier checkin today.


=== Added File Zope3/src/zope/app/utility/__init__.py ===
##############################################################################
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
##############################################################################
"""Local utility service implementation.

Besides being functional, this module also serves as an example of
creating a local service; see README.txt.

$Id: __init__.py,v 1.1 2004/03/11 22:05:01 srichter Exp $
"""
from zope.interface import implements
from persistent.dict import PersistentDict
from persistent import Persistent
from zope.app.component.nextservice import getNextService
from zope.app.interfaces.services.registration import IRegistry
from zope.app.interfaces.services.service import ISimpleService
from zope.app.utility.interfaces import \
     IUtilityRegistration, ILocalUtilityService
from zope.app.services.servicenames import Utilities
from zope.app.services.registration import \
     RegistrationStack, ComponentRegistration
from zope.component.exceptions import ComponentLookupError
from zope.interface.implementor import ImplementorRegistry
from zope.proxy import removeAllProxies
from zope.app.container.contained import Contained

class LocalUtilityService(Persistent, Contained):

    implements(ILocalUtilityService, IRegistry, ISimpleService)

    def __init__(self):
        self._utilities = PersistentDict()

    def getUtility(self, interface, name=''):
        """See zope.component.interfaces.IUtilityService"""
        utility = self.queryUtility(interface, name=name)
        if utility is None:
            raise ComponentLookupError("utility", interface, name)
        return utility

    def queryUtility(self, interface, default=None, name=''):
        """See zope.component.interfaces.IUtilityService"""
        stack = self.queryRegistrations(name, interface)
        if stack is not None:
            registration = stack.active()
            if registration is not None:
                return registration.getComponent()

        next = getNextService(self, Utilities)
        return next.queryUtility(interface, default, name)

    def getUtilitiesFor(self, interface):
        """See zope.component.interfaces.IUtilityService"""
        utilities = self.getLocalUtilitiesFor(interface)
        names = map(lambda u: u[0], utilities)

        next = getNextService(self, Utilities)
        for utility in next.getUtilitiesFor(interface):
            if utility[0] not in names:
                utilities.append(utility)
        return utilities

    def getLocalUtilitiesFor(self, interface):
        """See zope.app.interfaces.services.ILocalUtilityService"""
        utilities = []
        for name in self._utilities:
            for iface, cr in self._utilities[name].getRegisteredMatching():
                if not cr or cr.active() is None:
                    continue
                utility = cr.active().getComponent()
                if interface and not iface.extends(interface, 0) and \
                       removeAllProxies(utility) is not interface:
                    continue
                utilities.append((name, utility))
        return utilities


    def getRegisteredMatching(self, interface=None, name=None):
        """See zope.app.interfaces.services.ILocalUtilityService"""
        L = []
        for reg_name in self._utilities:
            for iface, cr in self._utilities[reg_name].getRegisteredMatching():
                if not cr:
                    continue
                if interface and not iface is interface:
                    continue
                if name is not None and reg_name.find(name) < 0:
                    continue
                L.append((iface, reg_name, cr))
        return L

    def queryRegistrationsFor(self, registration, default=None):
        """zope.app.interfaces.services.registration.IRegistry"""
        return self.queryRegistrations(registration.name,
                                        registration.interface,
                                        default)

    def queryRegistrations(self, name, interface, default=None):
        """zope.app.interfaces.services.registration.IRegistry"""
        utilities = self._utilities.get(name)
        if utilities is None:
            return default
        stack = utilities.getRegistered(interface)
        if stack is None:
            return default

        return stack

    def createRegistrationsFor(self, registration):
        """zope.app.interfaces.services.registration.IRegistry"""
        return self.createRegistrations(registration.name,
                                         registration.interface)


    def createRegistrations(self, name, interface):
        """zope.app.interfaces.services.registration.IRegistry"""
        utilities = self._utilities.get(name)
        if utilities is None:
            utilities = ImplementorRegistry(PersistentDict())
            self._utilities[name] = utilities

        stack = utilities.getRegistered(interface)
        if stack is None:
            stack = RegistrationStack(self)
            utilities.register(interface, stack)

        return stack


class UtilityRegistration(ComponentRegistration):
    """Utility component registration for persistent components

    This registration configures persistent components in packages to
    be utilities.
    """

    serviceType = 'Utilities'

    implements(IUtilityRegistration)

    def __init__(self, name, interface, component_path, permission=None):
        ComponentRegistration.__init__(self, component_path, permission)
        self.name = name
        self.interface = interface

    def usageSummary(self):
        # Override IRegistration.usageSummary()
        component = self.getComponent()
        s = self.getInterface().getName()
        if self.name:
            s += " registered as '%s'" % self.name
        s += ", implemented by %s" %component.__class__.__name__
        s += " '%s'"%self.componentPath
        return s

    def getInterface(self):
        # ComponentRegistration calls this when you specify a
        # permission; it needs the interface to create a security
        # proxy for the interface with the given permission.
        # XXX Smells like a dead chicken to me.
        return self.interface


# XXX Pickle backward compatability
UtilityConfiguration = UtilityRegistration


=== Added File Zope3/src/zope/app/utility/configure.zcml ===
<configure 
    xmlns="http://namespaces.zope.org/zope"
    xmlns:fssync="http://namespaces.zope.org/fssync">

  <!-- For backward compatibility -->

  <modulealias
      module="."
      alias="zope.app.services.utility"
      />

  <modulealias
      module=".interfaces"
      alias="zope.app.interfaces.services.utility"
      />

<content class=".LocalUtilityService">
  <factory
      id="zope.app.services.UtilityService"
      />
  <require
    permission="zope.ManageServices"
    attributes="getRegisteredMatching queryRegistrations"
    />
</content>

<content class=".UtilityRegistration">
  <require
    permission="zope.ManageServices"
    interface="zope.app.utility.interfaces.IUtilityRegistration
               zope.app.container.interfaces.IAddNotifiable
               zope.app.container.interfaces.IRemoveNotifiable"
    set_schema="zope.app.utility.interfaces.IUtilityRegistration"
    />
 </content>

<fssync:adapter
    class=".UtilityRegistration"
    factory="zope.app.services.registration.ComponentRegistrationAdapter"
    />

</configure>


=== Added File Zope3/src/zope/app/utility/interfaces.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Interfaces pertaining to local utilities.

$Id: interfaces.py,v 1.1 2004/03/11 22:05:01 srichter Exp $
"""
from zope.app.component.interfacefield import InterfaceField
from zope.app.i18n import ZopeMessageIDFactory as _
from zope.app.interfaces.services.registration import IComponentRegistration
from zope.app.interfaces.services.registration import IRegisterable
from zope.app.interfaces.services.registration import ComponentPath
from zope.component.interfaces import IUtilityService
from zope.schema import TextLine

class IUtilityRegistration(IComponentRegistration):
    """Utility registration object.

    This is keyed off name (which may be empty) and interface.  It
    overrides componentPath (to make it readonly); it also inherits a
    getComponent() method.
    """

    name = TextLine(
        title=_("Register As"),
        description=_("The name that is registered"),
        readonly=True,
        required=True,
        )

    interface = InterfaceField(
        title = _("Provided interface"),
        description = _("The interface provided by the adapter"),
        readonly = True,
        required = True,
        )

    componentPath = ComponentPath(
        title=_("Component path"),
        description=_("The physical path to the component"),
        required=True,
        readonly=True,
        )



class ILocalUtility(IRegisterable):
    """Local utility marker.

    A marker interface that indicates that a component can be used as
    a local utility.

    Utilities should usually also declare they implement
    IAttributeAnnotatable, so that the standard adapter to
    IRegistered can be used; otherwise, they must provide
    another way to be adaptable to IRegistered.
    """


class ILocalUtilityService(IUtilityService):
    """Local utility service interface.

    Methods which must be implemented by a local utility service to
    allow views to retrieve sufficient information from the service.
    """
    
    def getLocalUtilitiesFor(interface):
        """Returns all active (registered is not enough) utilities that are
        directly registered with this utility service.

        An empty list is returned, if no local utilities for this interface
        were found. If the interface is None, all utilities will be retruned.
        """ 

    def getRegisteredMatching(interface=None, name=None):
        """Return the registered utilities.

        The return value is an iterable object for which each item
        is a three-element tuple:

        - provided interface

        - name

        - registration stack

        One item is present for each registration.

        If interface is None, all registered registrations are returned.
        Otherwise, only registrations that provide the given interface
        are returned.

        Also, if name is provided and is contained in the name of the
        registered utility, we use that to filter the returned values.
        """


=== Added File Zope3/src/zope/app/utility/tests.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Utility service tests

$Id: tests.py,v 1.1 2004/03/11 22:05:01 srichter Exp $
"""
import unittest
from zope.app.tests import setup
from zope.app.services.tests import placefulsetup
from zope.app import utility
from zope.component.utility import utilityService as globalUtilityService
from zope.interface import Interface, implements
from zope.component import getService
from zope.component.exceptions import ComponentLookupError
from zope.app.traversing import traverse
from zope.app.interfaces.services.registration import IRegistrationStack
from zope.app.interfaces.services.registration import UnregisteredStatus
from zope.app.interfaces.services.registration import RegisteredStatus
from zope.app.interfaces.services.registration import ActiveStatus
from zope.app.utility.interfaces import ILocalUtility
from zope.app.interfaces.services.registration import IRegistered
from zope.app.interfaces.dependable import IDependable
from zope.app.tests import setup

class IFo(Interface): pass

class IFoo(IFo):
    def foo(self): pass

class IBar(Interface): pass


class Foo:
    # We implement IRegistered and IDependable directly to
    # depend as little  as possible on other infrastructure.
    __name__ = __parent__ = None
    implements(IFoo, ILocalUtility, IRegistered, IDependable)
    
    def __init__(self, name):
        self.name = name
        self._usages = []
        self._dependents = []

    def foo(self):
        return 'foo ' + self.name

    def addUsage(self, location):
        "See zope.app.interfaces.services.registration.IRegistered"
        if location not in self._usages:
            self._usages.append(location)

    def removeUsage(self, location):
        "See zope.app.interfaces.services.registration.IRegistered"
        self._usages.remove(location)

    def usages(self):
        "See zope.app.interfaces.services.registration.IRegistered"
        return self._usages

    def addDependent(self, location):
        "See zope.app.interfaces.dependable.IDependable"
        if location not in self._dependents:
            self._dependents.append(location)

    def removeDependent(self, location):
        "See zope.app.interfaces.dependable.IDependable"
        self._dependents.remove(location)

    def dependents(self):
        "See zope.app.interfaces.dependable.IDependable"
        return self._dependents

class TestUtilityService(placefulsetup.PlacefulSetup, unittest.TestCase):

    def setUp(self):
        sm = placefulsetup.PlacefulSetup.setUp(self, site=True)
        setup.addService(sm, "Utilities",
                         utility.LocalUtilityService())

    def test_queryUtility_delegates_to_global(self):
        globalUtilityService.provideUtility(IFoo, Foo("global"))
        globalUtilityService.provideUtility(IFoo, Foo("global bob"),
                                            name="bob")

        utility_service = getService(self.rootFolder, "Utilities")
        self.assert_(utility_service != globalUtilityService)

        self.assertEqual(utility_service.queryUtility(IFoo).foo(),
                         "foo global")
        self.assertEqual(utility_service.queryUtility(IFoo, name="bob").foo(),
                         "foo global bob")
        self.assertEqual(utility_service.queryUtility(IFo).foo(),
                         "foo global")
        self.assertEqual(utility_service.queryUtility(IFo, name="bob").foo(),
                         "foo global bob")

        self.assertEqual(utility_service.queryUtility(IBar), None)
        self.assertEqual(utility_service.queryUtility(IBar, name="bob"), None)
        self.assertEqual(utility_service.queryUtility(IFoo, name="rob"), None)

    def test_getUtility_delegates_to_global(self):
        globalUtilityService.provideUtility(IFoo, Foo("global"))
        globalUtilityService.provideUtility(IFoo, Foo("global bob"),
                                            name="bob")

        utility_service = getService(self.rootFolder, "Utilities")
        self.assert_(utility_service != globalUtilityService)

        self.assertEqual(utility_service.getUtility(IFoo).foo(),
                         "foo global")
        self.assertEqual(utility_service.getUtility(IFoo, name="bob").foo(),
                         "foo global bob")
        self.assertEqual(utility_service.getUtility(IFo).foo(),
                         "foo global")
        self.assertEqual(utility_service.getUtility(IFo, name="bob").foo(),
                         "foo global bob")


        self.assertRaises(ComponentLookupError,
                          utility_service.getUtility, IBar)
        self.assertRaises(ComponentLookupError,
                          utility_service.getUtility, IBar, name='bob')
        self.assertRaises(ComponentLookupError,
                          utility_service.getUtility, IFoo, name='rob')


    def test_registrationsFor_methods(self):
        utilities = getService(self.rootFolder, "Utilities")
        default = traverse(self.rootFolder, "++etc++site/default")
        default['foo'] = Foo("local")
        path = "/++etc++site/default/foo"

        for name in ('', 'bob'):
            registration = utility.UtilityRegistration(name, IFoo, path)
            self.assertEqual(utilities.queryRegistrationsFor(registration),
                             None)
            registery = utilities.createRegistrationsFor(registration)
            self.assert_(IRegistrationStack.providedBy(registery))
            self.assertEqual(utilities.queryRegistrationsFor(registration),
                             registery)


    def test_local_utilities(self):
        globalUtilityService.provideUtility(IFoo, Foo("global"))
        globalUtilityService.provideUtility(IFoo, Foo("global bob"),
                                            name="bob")

        utilities = getService(self.rootFolder, "Utilities")
        default = traverse(self.rootFolder, "++etc++site/default")
        default['foo'] = Foo("local")
        path = "/++etc++site/default/foo"
        cm = default.getRegistrationManager()

        for name in ('', 'bob'):
            registration = utility.UtilityRegistration(name, IFoo, path)
            cname = cm.addRegistration(registration)
            registration = traverse(cm, cname)

            gout = name and "foo global "+name or "foo global"

            self.assertEqual(utilities.getUtility(IFoo, name=name).foo(), gout)

            registration.status = ActiveStatus

            self.assertEqual(utilities.getUtility(IFoo, name=name).foo(),
                             "foo local")

            registration.status = RegisteredStatus

            self.assertEqual(utilities.getUtility(IFoo, name=name).foo(), gout)

    def test_getRegisteredMatching(self):
        self.test_local_utilities()
        utilities = getService(self.rootFolder, "Utilities")
        r = list(utilities.getRegisteredMatching())
        r.sort()
        path = "/++etc++site/default/foo"
        cr1 = utilities.queryRegistrationsFor(
            utility.UtilityRegistration("", IFoo, path))
        cr2 = utilities.queryRegistrationsFor(
            utility.UtilityRegistration("bob", IFoo, path))
        self.assertEqual(r, [(IFoo, "", cr1), (IFoo, "bob", cr2)])
        self.assertEqual(r[0][2].__parent__, utilities)
        self.assertEqual(r[1][2].__parent__, utilities)
        # Now test that an empty registry doesn't show up
        for cd in cr1.info(): # Remove everything from cr1
            cd['registration'].status = UnregisteredStatus
        self.assertEqual(bool(cr1), False)
        r = list(utilities.getRegisteredMatching())
        self.assertEqual(r, [(IFoo, "bob", cr2)])


def test_suite():
    return unittest.makeSuite(TestUtilityService)


if __name__ == '__main__':
    unittest.main()


=== Added File Zope3/src/zope/app/utility/utility.txt ===
==================================================
Creating Local Services: The Local Utility Service
==================================================

:Author: Jim Fulton
:Version: $Revision: 1.1 $

. contents::

This document describes how to implement a common type of local
service.  We'll walk through an example step by step.  We'll implement
a local utility service.  A utility service must implement the
interface ``zope.component.interfaces.IUtilityService``.

Step 1. Create a minimal service
--------------------------------

Create a minimal service that delagates everything to the
service above it, in the file ``utility.py``::

  from persistence import Persistent
  from zope.component.exceptions import ComponentLookupError
  from zope.app.component.nextservice import getNextService
  from zope.component.interfaces import IUtilityService
  from zope.app.interfaces.services.interfaces import ISimpleService
  from zope.interface import implements
  from zope.app.container.contained import Contained

  class LocalUtilityService(Persistent, Contained):

      implements(IUtilityService, ISimpleService)

      def getUtility(self, interface, name=''):
          utility = self.queryUtility(interface, name=name)
          if utility is None:
              raise ComponentLookupError("utility", interface, name)
          return utility

      def queryUtility(self, interface, default=None, name=''):
          next = getNextService(self, "Utilities")
          return next.queryUtility(interface, default, name)

The local service subclasses ``Persistent`` to provide support for
transparent persistent in the ZODB.

The local service also subclasses ``Contained''.  ``Contained'' is a
simple base class that defines two attributes, ``__parent__'' and
``__name__'' and declares implementation of the ``IContained''
interface.  The containment framework is used to keep track of object
locations. If objects don't care about containment, they needn't
implement this interface. Their containes will use containment proxies
to support the framework. If objects need to use location information,
then they need to implement ``IContained''.  This is needed to call
most component apis.

The ``getUtility`` method simply delegates to ``queryUtility``.  The
``queryUtility`` method delegates to the next utility service using
``getNextService``.  (Both methods are specified by the
``IUtilityService`` interface.)

The function ``getNextService`` looks up the next service above the
current service.  It takes a location and a service name.  We use it
to get the interface service defined above our service, which may be
the global service, and delegate to it.

In addition to implementing ``IUtilityService``, the local service
implements ``ISimpleService``.  A Local service must implement
``zope.app.interfaces.services.interfaces.ILocalService`` and a local
service must be annotatable.  ``ISimpleService`` simply extends
``ILocalService`` and ``IAttributeAnnotatable``.

I created the service in the ``utility`` module in this package (the
file ``utility.py``).  This package is already pretty large.  To avoid
a really large zcml file, I've started giving each service its own
zcml file.  So I also created an ``utility.zcml`` file::

  <configure xmlns="http://namespaces.zope.org/zope">

  <content class=".utility.LocalUtilityService">
    <factory
        id="zope.app.services.UtilityService"
        permission="zope.ManageServices"
        />
  </content>

  </configure>

and I added an include to the package configuration file::

  <!-- Utility Service --> <include file="utility.zcml" />

XXX This next section is out of date, and should be rewritten to
use addMenuItem.

To make it possible to add the utility service, I need to add an entry to
the ``add_service`` browser menu.  The ``add_component`` menu is the menu
used by site folders for adding objects.  To do this, I need to add a
browser menu configuration.  Eventually, the local interface will
have a number of views, so I create a package, ``utility``, for
it in ``zope/app/browser/services``.  [1]_ In that
package, I put a configuration that defines the needed browser menu
item::

   <configure xmlns="http://namespaces.zope.org/browser">

   <menuItem
         for="zope.app.container.interfaces.IAdding"
         menu="add_service"
         action="zope.app.services.UtilityService"
         title="Utility Service"
         permission="zope.ManageServices"
         />

   </configure>

and I added an include to the configuration file in
zope.app.browser.services::

   <!-- Utility Service --> <include package=".utility" />

With this in place, I can add a local service that does nothing but
delegate to a service above it.  (To actually create a utility service
instance, I have to go to the service manager and use its ``Add
service`` action.  The service manager is reached from the root folder
by using the ``Manage local services`` action.)


Step 2. Providing functionality
-------------------------------

Now it's time to add some functionality.  A utility service keeps
track of utility components by name and interface.  It allows
components to be registered and then looked up later.

We'll start by updating the utility service to support registrations.
The updated local utility service implementation can be found in
``zope/app/services/utility.py``. It's a good idea to refer to the
source file as you read the description here.

First, we'll pick a data structure.  We'll use a persistent dictionary
mapping utility names to implementor registries.  An implementor
registry implements a mapping from interfaces to objects; it's a
special mapping because it understands subclassing relationships
between the interfaces used as keys.  In this case, the implementor
registries themselves map interfaces to RegistrationStacks. The
overall data structure looks like::

  { utility_name -> { interface -> stack } }

We also need to implement
``zope.app.interfaces.services.registration.IRegistry``.  This defines
two methods, ``queryRegistrationsFor`` and ``createRegistrationsFor``.
The ``queryRegistrationsFor`` method takes a registration object and
returns the corresponding registration stack.  The registration
object is used to provide an abstract way to represent registration
parameters.  Typically, the registration parameters are extracted and
a more concrete method is called.  In the local utility service, we
extract the utility name and interface and call ``queryRegistrations``
with the name and interface.

Similarly, the``createRegistrationsFor`` method takes a
registration object holding registration parameters and creates a
registration stack for the parameters (if none already exists).
If we don't have a implementor registry for a utility name, we create
one and add it.  When we create the implementor registry, we pass a
``PersistentDict`` for it to use to store registration data.  This
assures that updates are made persistently.  If there isn't implementor
data for the given interface, we create a registration registry and
register it for the interface.

Finally, we modify ``queryUtility`` to use registered utilities.  We
try to get a registration stack by calling ``queryRegistrations``.  If
we get one, we call its ``active`` method to get the active
registration, if any.  Finally, we call ``getComponent`` on the active
registration to get the actual component.  We leave it up to the
registration object to take care of actually finding and returning the
component.

In addition to the utility service, We need to provide utility
registration objects.  The utility registration objects need to manage
several bits of information:

- name

- interface

- permission

- The location of the actual component.

The registration objects provide access to the component through the
``getComponent`` method.

To create the registration class, we'll start by defining a
registration schema in ``zope/app/interfaces/services/utility.py``.
The schema should extend
``zope.app.interfaces.services.registration.IRegistration``.  There's
a more specific interface,
``zope.app.interfaces.services.registration.IComponentRegistration``
that is much closer to what we need. [2]_ We extend this interface as
IUtilityRegistration, which adds a name field (which is required but
may be empty -- note the subtle difference, because the empty string
is still used as part of the lookup key) and an interface field.  We
also override the componentPath field to make it read-only (this is
for the UI definition).

A ``UtilityRegistration`` class is added to the ``utility`` module in
``zope/app/services`` that implements the registration interface.  We
can subclass ComponentRegistration, which does much of the work.  The
class definition includes a ``serviceType`` attribute. This attribute
is used by the registration framework to decide which service a
registration is used with.  The class includes two methods, defined in
``IRegistration``, giving summary information used by several
predefined views.  See the interface for a description of these
methods.

We need to provide user interfaces for:

- The utility service, 

- The utility registrations, and for 

- Registered utilities

Utility service user interface
==============================

The utility service needs a view for showing what utilities have been
registered. 

MORE DOCUMENTATION TO FOLLOW 
(well, not really, we're just saying that ;)

Utility registration user interface
===================================

We need two views for registration objects:

- an add view, and 

- an edit view

The registration add view will be created using a schema-driven add
form::

  <addform
      label="New Utility Registration"
      for="zope.app.interfaces.services.utility.ILocalUtility"
      name="addRegistration.html"
      schema="zope.app.interfaces.services.utility.IUtilityRegistration"
      class=".AddRegistration"
      permission="zope.ManageServices"
      content_factory="zope.app.services.utility.UtilityRegistration"
      arguments="name interface componentPath"
      set_after_add="status"
      fields="name interface componentPath permission status"
      />

The add form is named ``addRegistration.html``.  This is a standard
name that a number of other views expect to be defined.  The form is
registered for ``ILocalUtility``.  ``ILocalUtility`` is a marker
interface that extends ``IRegisterable``.  We'll require that all
local utilities implement this view.  Utility components should also
implement IAttributeAnnotatable, unless they want to provide a
different way to store annotations.

Notice that there's no template!  The <addform> directive creates the
form for us using a generic template, zope/app/browser/form/add.pt,
and information about the specific fields to be displayed extracted
from the schema.  We do specify a class name: the AddRegistration
class.  This class needs some explanation.

The <addform> directive uses the AddRegistration class as a mix-in
class.  It may override various methods to customize the add form; the
set of methods that can be customized is described by the
``zope.app.browser.interfaces.form.IAddFormCustomization`` interface.
In this particular case, we must override ``add`` and ``nextURL``
because their default implementations only work when the add form is a
view on an `IAdding` view.  That is the normal way to use add forms, but
here we don't do that; this particular add form is a view on a local
utility component.  Our ``AddRegistration`` class subclasses
``zope.app.browser.services.registration.AddComponentRegistration``,
which provides the implementations of ``add`` and ``nextURL`` that we
need. The ``add`` method defined in ``AddComponentRegistration`` finds
the congiguration manager in the current folder and adds the new
registration object to it.

The AddRegistration class defines a class attribute::

    interface_widget = CustomWidgetFactory(UtilityInterfaceWidget)

This tells the forms machinery to use a a custom widget for the
interface field. The custom widget we use is a specialized interface
widget that allows the user to select from the non-trivial interfaces
implemented by the component being configured.

The edit view looks similar to the add view, but its definition
is simpler, because it isn't deviating quite as much from a standard
edit view::

  <editform
      name="index.html"
      menu="zmi_views" title="Edit"
      schema="zope.app.interfaces.services.utility.IUtilityRegistration"
      label="Utility Registration"
      permission="zope.ManageServices"
      fields="name interface componentPath permission status"
      />

This is a view on IUtilityRegistration, which is typical for an edit
view.  The good news is that it has no template *or* class!  The
<editform> directive lets us specifiy all the customization we need:

- ``name=``: The view name.  This is the last component of the URL for
  the view.

- ``menu=``, ``title=``: Menu information: "zmi_views" means that this
  view is one of the "tabs" for this type of object, the title
  argument gives the text in the tab.

- ``schema=``: The interface used as the schema.  The field
  definitions in this interface define which attributes are displayed
  in the form, and how.

- ``label=``: The label, used as the title text in the view.

- ``permission=``: A permission needed to access the view.

- ``fields=``: A list of fields to be displayed in the form.  This is
  used here to force the order in which the fields are displayed.  It
  can also be used to display only a subset of the fields present in
  the schema.

And that's all there is to the edit view.  Some observable differences
between the edit view and the add view:

- The add view lets you specify the name or the interface; the edit
  view displays these fields read-only.

- When you submit the add view, you are redirected to the
  registration manager; the edit view takes you back to itself.


User interface for registered utilities
=======================================

In general, for registerable components, we want to do
the registration through the components themselves.  That is, the site
admin should be able to walk up to a component and add or change a
registration for it.  The most common case is actually that a site
manager creates a component and configures it right away.  (This is
the same common case that is used for creating and configuring
services.)  There's a standard view registered for all IRegisterable
components that provides the ability to view and change the
registrations for a component. 

IRegisterable is a marker interface that extends IAnnotatable.
Annotations are used to record the registrations for a component.

---------------------------------------------------------------

. [1] Of course, I initially forgot to include a nearly empty
   ``__init__.py`` file and had to add one later.

. [2] It's tempting to use ``INamedComponentRegistration`, but
   ``INamedComponentRegistration`` is based on ``INamedRegistration``,
   which is for registring components looked up by name alone.
   ``INamedRegistration`` requires a non-empty name, but we want to
   allow empty names, as we are looking up objects based on **both**
   name and interface.
 




More information about the Zope3-Checkins mailing list