summaryrefslogtreecommitdiff
path: root/repoze/bfg/traversal.py
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-01-27 21:57:11 +0000
committerChris McDonough <chrism@agendaless.com>2009-01-27 21:57:11 +0000
commite62e479e338e428f6cfd3b07790545982b7cb94f (patch)
treec9784577f791d4a8ea5b80a9fce211ce86009712 /repoze/bfg/traversal.py
parent2301cf61977102b85279ea7c04797f76012202e5 (diff)
downloadpyramid-e62e479e338e428f6cfd3b07790545982b7cb94f.tar.gz
pyramid-e62e479e338e428f6cfd3b07790545982b7cb94f.tar.bz2
pyramid-e62e479e338e428f6cfd3b07790545982b7cb94f.zip
Features
-------- - The ``repoze.bfg.url.model_url`` API now works against contexts derived from Routes URL dispatch (``Routes.util.url_for`` is called under the hood). - "Virtual root" support for traversal-based applications has been added. Virtual root support is useful when you'd like to host some model in a :mod:`repoze.bfg` model graph as an application under a URL pathname that does not include the model path itself. For more information, see the (new) "Virtual Hosting" chapter in the documentation. - A ``repoze.bfg.traversal.virtual_root`` API has been added. When called, it returns the virtual root object (or the physical root object if no virtual root has been specified). Implementation Changes ---------------------- - ``repoze.bfg.traversal.RoutesModelTraverser`` has been moved to ``repoze.bfg.urldispatch``. - ``model_url`` URL generation is now performed via an adapter lookup based on the context and the request. - ZCML which registers two adapters for the ``IContextURL`` interface has been added to the configure.zcml in ``repoze.bfg.includes``.
Diffstat (limited to 'repoze/bfg/traversal.py')
-rw-r--r--repoze/bfg/traversal.py114
1 files changed, 88 insertions, 26 deletions
diff --git a/repoze/bfg/traversal.py b/repoze/bfg/traversal.py
index d6f92e118..e875219c1 100644
--- a/repoze/bfg/traversal.py
+++ b/repoze/bfg/traversal.py
@@ -1,5 +1,7 @@
import urllib
+from zope.component import getMultiAdapter
+
from zope.deferredimport import deprecated
from zope.interface import classProvides
@@ -9,10 +11,13 @@ from repoze.bfg.location import LocationProxy
from repoze.bfg.location import lineage
from repoze.bfg.lru import lru_cache
+from repoze.bfg.url import _urlsegment
+from repoze.bfg.interfaces import IContextURL
from repoze.bfg.interfaces import ILocation
from repoze.bfg.interfaces import ITraverser
from repoze.bfg.interfaces import ITraverserFactory
+from repoze.bfg.interfaces import VH_ROOT_KEY
deprecated(
"('from repoze.bfg.traversal import model_url' is now "
@@ -20,6 +25,13 @@ deprecated(
model_url = "repoze.bfg.url:model_url",
)
+deprecated(
+ "('from repoze.bfg.traversal import RoutesModelTraverser' is now "
+ "deprecated; instead use 'from repoze.bfg.urldispatch "
+ "import RoutesModelTraverser')",
+ RoutesModelTraverser = "repoze.bfg.urldispatch:RoutesModelTraverser",
+ )
+
# ``split_path`` wasn't actually ever an API but people were using it
# anyway. I turned it into the ``traversal_path`` API in 0.6.5, and
# generate the below deprecation to give folks a heads up.
@@ -109,6 +121,34 @@ def model_path(model, *elements):
path = '/'.join([path, suffix])
return path
+def virtual_root(model, request):
+ """ Return the model object representing the 'virtual root' of the
+ current request. Using a virtual root in a traversal-based
+ :mod:`repoze.bfg` application permits rooting, for example, the
+ object at the traversal path ``/cms`` at ``http://example.com/``
+ instead of rooting it at ``http://example.com/cms/``.
+
+ If the ``model`` passed in is a context obtained via
+ :term:`traversal`, and if the ``%s`` key is in the WSGI
+ environment, the value of this key will be treated as a 'virtual
+ root path': the :mod:``repoze.bfg.traversal.find_model`` API will
+ be used to find the virtual root object using this path; if the
+ object is found, it will found will be returned. If the ``%s``
+ key is is not present in the WSGI environment, the physical
+ :term:`root` of the graph will be returned instead.
+
+ .. note:: Virtual roots are not useful in at all applications that
+ use :term:`URL dispatch`. Contexts obtained via URL
+ dispatch don't really support being virtually rooted
+ (each URL dispatch context is both its own physical and
+ virtual root). However, for symmetry, if this API is
+ called with a model which is a context obtained via URL
+ dispatch, the model passed in will be returned
+ unconditonally.
+ """ % (VH_ROOT_KEY, VH_ROOT_KEY)
+ urlgenerator = getMultiAdapter((model, request), IContextURL)
+ return urlgenerator.virtual_root()
+
@lru_cache(500)
def traversal_path(path):
""" Given a PATH_INFO string (slash-separated path elements),
@@ -217,35 +257,57 @@ class ModelGraphTraverser(object):
except KeyError:
return name, default
-class RoutesModelTraverser(object):
- classProvides(ITraverserFactory)
- implements(ITraverser)
- def __init__(self, context):
+class TraversalContextURL(object):
+ """ The IContextURL adapter used to generate URLs for a context
+ object obtained via graph traversal"""
+ implements(IContextURL)
+
+ vroot_varname = VH_ROOT_KEY
+
+ def __init__(self, context, request):
self.context = context
+ self.request = request
- def __call__(self, environ):
- # the traverser *wants* to get routing args from the environ
- # as of 0.6.5; the rest of this stuff is for backwards
- # compatibility
- try:
- # 0.6.5 +
- routing_args = environ['wsgiorg.routing_args'][1]
- except KeyError:
- # <= 0.6.4
- routing_args = self.context.__dict__
+ def virtual_root(self):
try:
- view_name = routing_args['view_name']
+ vroot_path = self.request.environ[self.vroot_varname]
except KeyError:
- # b/w compat < 0.6.3
+ # shortcut instead of using find_root; we probably already
+ # have it on the request
try:
- view_name = routing_args['controller']
- except KeyError:
- view_name = ''
- try:
- subpath = routing_args['subpath']
- subpath = filter(None, subpath.split('/'))
- except KeyError:
- # b/w compat < 0.6.5
- subpath = []
+ return self.request.root
+ except AttributeError:
+ return find_root(self.context)
+ return find_model(self.context, vroot_path)
+
+ def __call__(self):
+ """ Generate a URL based on the lineage of a model obtained
+ via traversal. If any model in the context lineage has a
+ unicode name, it will be converted to a UTF-8 string before
+ being attached to the URL. When composing the path based on
+ the model lineage, empty names in the model graph are ignored.
+ If a ``%s`` key is present in the WSGI environment, its value
+ will be treated as a 'virtual root path': the path of the URL
+ generated by this will be left-stripped of this virtual root
+ path value.
+ """ % VH_ROOT_KEY
+ rpath = []
+ for location in lineage(self.context):
+ name = location.__name__
+ if name:
+ rpath.append(_urlsegment(name))
+ if rpath:
+ path = '/' + '/'.join(reversed(rpath)) + '/'
+ else:
+ path = '/'
+ request = self.request
+ # if the path starts with the virtual root path, trim it out
+ vroot_path = request.environ.get(self.vroot_varname)
+ if vroot_path is not None:
+ if path.startswith(vroot_path):
+ path = path[len(vroot_path):]
+
+ app_url = request.application_url # never ends in a slash
+ return app_url + path
+
- return self.context, view_name, subpath