Source code for grokcore.traverser.components
##############################################################################
#
# Copyright (c) 2006-2007 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Base classes for Grok application components.
When an application developer builds a Grok-based application, the
classes they define each typically inherit from one of the base classes
provided here.
"""
import zope.interface
from grokcore.component.interfaces import IContext
from zope import component
from zope.container.interfaces import IContainer
from zope.container.interfaces import IReadContainer
from zope.publisher.defaultview import getDefaultViewName
from zope.publisher.interfaces import NotFound
from zope.publisher.interfaces.browser import IBrowserPublisher
from zope.publisher.interfaces.http import IHTTPRequest
import grokcore.traverser
from grokcore.traverser.interfaces import IRESTLayer
from grokcore.traverser.util import safely_locate_maybe
[docs]
@zope.interface.implementer(IBrowserPublisher)
class Traverser:
"""Base class for traversers in Grok applications."""
def __init__(self, context, request):
self.context = context
self.request = request
[docs]
def browserDefault(self, request):
# if we have a RESTful request, we will handle
# GET, POST and HEAD differently (PUT and DELETE are handled already
# but not on the BrowserRequest layer but the HTTPRequest layer)
if IRESTLayer.providedBy(request):
rest_view = component.getMultiAdapter(
(self.context, self.request), name=request.method)
return rest_view, ()
view_name = getDefaultViewName(self.context, request)
view_uri = "@@%s" % view_name
return self.context, (view_uri,)
[docs]
def publishTraverse(self, request, name):
subob = self.traverse(name)
if subob is not None:
return safely_locate_maybe(subob, self.context, name)
traversable_dict = grokcore.traverser.traversable.bind().get(
self.context)
if traversable_dict:
if name in traversable_dict:
subob = getattr(self.context, traversable_dict[name])
if callable(subob):
subob = subob()
return safely_locate_maybe(subob, self.context, name)
# XXX Special logic here to deal with containers. It would be
# good if we wouldn't have to do this here. One solution is to
# rip this out and make you subclass ContainerTraverser if you
# wanted to override the traversal behaviour of containers.
if IReadContainer.providedBy(self.context):
item = self.context.get(name)
if item is not None:
return item
view = component.queryMultiAdapter((self.context, request), name=name)
if view is not None:
return view
raise NotFound(self.context, name, request)
[docs]
def traverse(self, name):
# this will be overridden by subclasses
pass
class ContextTraverser(Traverser):
"""Base class for context traversers in Grok applications.
A context traverser is like a normal `grok.Traverser` but, instead
of supplying its own `traverse()` method, it directs Grok to go call
the ``traverse()`` method on the context itself in order to process
the next name in the URL.
"""
component.adapts(IContext, IHTTPRequest)
def traverse(self, name):
traverse = getattr(self.context, 'traverse', None)
if traverse:
return traverse(name)
class ContainerTraverser(Traverser):
"""Base class for container traversers in Grok applications.
A container traverser is like a normal `grok.Traverser` but, instead
of supplying its own ``traverse()`` method, Grok will either call
the ``traverse()`` method on the context itself, if any, else call
``get()`` on the container (a getitem-style lookup) in order to
resolve the next name in the URL.
"""
component.adapts(IContainer, IHTTPRequest)
def traverse(self, name):
traverse = getattr(self.context, 'traverse', None)
if traverse:
result = traverse(name)
if result is not None:
return result
# try to get the item from the container
return self.context.get(name)