[Zope3-checkins] CVS: Zope3/src/zope/component - presentation.py:1.1.2.1

Jim Fulton cvs-admin at zope.org
Mon Nov 10 10:00:43 EST 2003


Update of /cvs-repository/Zope3/src/zope/component
In directory cvs.zope.org:/tmp/cvs-serv19095/src/zope/component

Added Files:
      Tag: adaptergeddon-branch
	presentation.py 
Log Message:
Oops, forgot to add these files when I created the global presentation
service.


=== Added File Zope3/src/zope/component/presentation.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.
#
##############################################################################
"""XXX short summary goes here.

XXX longer description goes here.

$Id: presentation.py,v 1.1.2.1 2003/11/10 15:00:42 jim Exp $
"""

from zope.component.interfaces import IPresentationService
import zope.interface
import zope.interface.surrogate


class IGlobalPresentationService(zope.interface.Interface):
    """Provide abaility to update the flobal presentation service
    """

    def defineSkin(name, layers):
        """Define a skin

        A skin is defined for a request type.  It consists of a
        sequence of layer names.  Layers must be defined before they
        are used in a skin definition.

        Note that there is one predefined layer, "default".
        """

    def setDefaultSkin(name):
        """Set the default skin for a request type

        If not set, it defaults to the "default" skin.
        """

    def defineLayer(name):
        """Define a layer
        """

    def provideAdapter(request_type, factories, name='', contexts=(),
                       providing=zope.interface.Interface, layer='default'):
        """Provide a presentation adapter
        """

class IDefaultViewName(zope.interface.Interface):
    """A string that contains the default view name

    A default view name is used to select a view when a user hasn't
    specified one.
    """

class GlobalPresentationService(object):
    """Global presentation service

    The global presentation service provides management of views, and
    resources arranged in skins, where skins are ordered collections
    of layers.

    Views are modeled as adapters of objects and requests.
    Resources are just request adapters.

    The adapters are arranged in layers.

    Let's look at some examples. First, we'll create a service:

    >>> s = GlobalPresentationService()

    And define a custom layer and skin:

    >>> s.defineLayer('custom')
    >>> s.defineSkin('custom', ['custom', 'default'])

    We'll define a request type and a fake request:

    >>> class IRequest(zope.interface.Interface):
    ...     "Demonstration request type"

    >>> class Request:
    ...     zope.interface.implements(IRequest)
    ...     def getPresentationSkin(self):
    ...         return getattr(self, 'skin', None)

    
    >>> request = Request()

    With this in place, we can start registering resources. A resource
    is just a request adapter.

    >>> class MyResource:
    ...    def __init__(self, request):
    ...        self.request = request

    To register a resource, we register it as an adapter. Most
    resources are going to interface with a user, and, so, don't
    really provide a programatic interface. For this reason, we
    register them to provide the empty interface, Interface, which is
    the default provided interface:

    >>> s.provideAdapter(IRequest, [MyResource], name='foo', layer='custom')

    Now we can try to look this up:

    >>> s.queryResource('foo', request)

    But we won't get anything, because our request doesn't specify a
    skin and, the default skin gets used.  Our resource was registered
    in the custom layer, which isn't used by the default skin. If we
    set out request skin to 'custom':

    >>> request.skin = 'custom'

    Then the lookup will suceed:

    >>> r = s.queryResource('foo', request)
    >>> r.__class__.__name__
    'MyResource'
    >>> r.request is request
    True

    Views are registered as "multi" adapters.  Multi-adapters adapt
    multiple objects simultaneously.

    >>> class IContact(zope.interface.Interface):
    ...     "Demonstration content type"

    >>> class MyView:
    ...     def __init__(self, context, request):
    ...         self.context, self.request = context, request

    >>> s.provideAdapter(IRequest, [MyView], contexts=[IContact], name='foo',
    ...                  layer='custom')

    When defining views, we provide one or more (typically 1) context
    interfaces, corresponding to the contexts of the view.

    >>> class Contact:
    ...     zope.interface.implements(IContact)

    >>> c = Contact()

    We look up views with queryView:

    >>> v = s.queryView(c, 'foo', request)
    >>> v.__class__.__name__
    'MyView'
    >>> v.request is request
    True
    >>> v.context is c
    True

    Most views and resources are unnamed and provide no interface. We
    can also have views that provide interfaces.  For example, we
    might need a view to help out with finding objects:

    >>> class ITraverse(zope.interface.Interface):
    ...     "Sample traversal interface (imagine interesting methods :)"

    >>> class Traverser:
    ...     zope.interface.implements(ITraverse)
    ...     def __init__(self, context, request):
    ...         self.context, self.request = context, request

    which we register using the provided interface, rather than a name. 

    >>> s.provideAdapter(IRequest, [Traverser], contexts=[IContact],
    ...                  providing=ITraverse, layer='custom')

    (We could use a name too, if we wanted to.)

    Then we look up the view using the interface:

    >>> v = s.queryView(c, '', request, providing=ITraverse)
    >>> v.__class__.__name__
    'Traverser'
    >>> v.request is request
    True
    >>> v.context is c
    True
    

    """

    zope.interface.implements(IPresentationService, IGlobalPresentationService)
    

    def __init__(self):
        self._layers = {'default':
                        zope.interface.surrogate.SurrogateRegistry(),
                        }

        self._skins = {'default': [self._layers['default']]}
        self.skins = [('default', ('default', ))]

        self.defaultSkin = 'default'

    def defineSkin(self, name, layers):
        """Define a skin

        A skin is defined for a request type.  It consists of a
        sequence of layer names.  Layers must be defined before they
        are used in a skin definition.

        Note that there is one predefined layer, "default".

        >>> s = GlobalPresentationService()
        >>> s.defineSkin('default', ['default'])
        Traceback (most recent call last):
        ...
        ValueError: ("Can\'t redefine skin", 'default')


        The layers used in a skin definition must be defined before
        they are used:

        >>> s.defineSkin('custom', ['custom', 'default'])
        Traceback (most recent call last):
        ...
        ValueError: ('Undefined layers', ['custom'])


        >>> s.defineLayer('custom')
        >>> s.defineSkin('custom', ['custom', 'default'])

        >>> s.skins
        [('custom', ('custom', 'default')), ('default', ('default',))]
        """

        if name in self._skins:
            raise ValueError("Can't redefine skin", name)

        bad = [layer for layer in layers if layer not in self._layers]
        if bad:
            raise ValueError, ("Undefined layers", bad)

        self._skins[name] = [self._layers[layer] for layer in layers]
        self.skins.append((name, tuple(layers)))
        self.skins.sort()

    def setDefaultSkin(self, name):
        """Set the default skin for a request type

        If not set, it defaults to the "default" skin.

        >>> s = GlobalPresentationService()
        >>> s.defaultSkin
        'default'

        >>> s.setDefaultSkin('custom')
        Traceback (most recent call last):
        ...
        ValueError: ('Undefined skin', 'custom')

        >>> s.defineLayer('custom')
        >>> s.defineSkin('custom', ['custom', 'default'])
        >>> s.setDefaultSkin('custom')
        >>> s.defaultSkin
        'custom'

        """

        # Make sure we are refering to a defined skin
        if name not in self._skins:
            raise ValueError, ("Undefined skin", name)

        self.defaultSkin = name

    def defineLayer(self, name):
        """Define a layer

        >>> s = GlobalPresentationService()
        >>> s.defineLayer('custom')

        You can't define a layer that's already defined:
        
        >>> s.defineLayer('custom')
        Traceback (most recent call last):
        ...
        ValueError: ("Can\'t redefine layer", 'custom')

        """

        if name in self._layers:
            raise ValueError("Can\'t redefine layer", name)

        self._layers[name] = zope.interface.surrogate.SurrogateRegistry()

    def provideAdapter(self, request_type, factories, name=u'', contexts=(), 
                       providing=zope.interface.Interface, layer='default'):
        """Provide a presentation adapter

        This is a fairly low-level interface that supports both
        resources and views.
        
        """
        
        reg = self._layers[layer]
        ifaces = tuple(contexts) + (request_type, )
        reg.provideAdapter(ifaces[0], providing, factories, name, ifaces[1:])

    def queryResource(self, name, request, default=None):
        """Look up a named resource for a given request
        
        The request must implement IPresentationRequest.
        
        The default will be returned if the component can't be found.
        """
        skin = request.getPresentationSkin() or self.defaultSkin
        for layer in self._skins[skin]:
            r = layer.queryNamedAdapter(request, zope.interface.Interface,
                                        name)
            if r is not None:
                return r
        return default

    def queryView(self, object, name, request,
                  providing=zope.interface.Interface, default=None):
        """Look for a named view for a given object and request

        The request must implement IPresentationRequest.

        The default will be returned if the component can't be found.
        """
        skin = request.getPresentationSkin() or self.defaultSkin
        objects = object, request
        for layer in self._skins.get(skin, ()):
            r = layer.queryMultiAdapter(objects, providing, name)
            if r is not None:
                return r
        return default
        

    def queryMultiView(self, objects, name, request,
                       providing=zope.interface.Interface, default=None):
        """Adapt the given objects and request

        The first argument is a sequence of objects to be adapted with the
        request.
        """
        skin = request.getPresentationSkin() or self.defaultSkin
        objects = objects + (request, )
        for layer in self._skins[skin]:
            r = layer.queryMultiAdapter(objects, providing, name)
            if r is not None:
                return r
        return default

    ############################################################
    #
    # The following methods are provided for convenience and for
    # backward compatability with old code:

    def provideView(self, for_, name, type, maker, layer='default'):
        # Helper function for simple view defs
        if not isinstance(maker, (list, tuple)):
            maker = [maker]
        return self.provideAdapter(type, maker, name,
                                   contexts=[for_], layer=layer)


    def setDefaultViewName(self, for_, request_type, name, layer="default"):
        """Default view names

        A default view name is a name that an application should use
        if a user hasn't selected one.  This should not be confused
        with unnamed views.

        The presentation service can store this by storing the name as
        an "adapter".
        """
        return self.provideAdapter(request_type, name,
                                   providing=IDefaultViewName,
                                   contexts=[for_], layer=layer)

    def queryDefaultViewName(self, object, request, default=None):
        skin = request.getPresentationSkin() or 'default'
        objects = object, request
        for layer in self._skins[skin]:
            r = layer.queryMultiAdapter(objects, IDefaultViewName, raw=True)
            if r is not None:
                return r
        return default

    def provideResource(self, name, request_type, factory, layer='default'):
        # Helper function for simple view defs
        if not isinstance(factory, (list, tuple)):
            factory = [factory]
        return self.provideAdapter(request_type, factory, name, layer=layer)




More information about the Zope3-Checkins mailing list