[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