From 35ff8e696a2c983cee119c936ea12ecb2f7da2c3 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Tue, 29 Jul 2008 01:54:50 +0000 Subject: 0.2.5: add model_url. --- CHANGES.txt | 4 ++++ docs/api/traversal.rst | 1 + docs/conf.py | 4 ++-- docs/glossary.rst | 3 ++- docs/narr/models.rst | 2 ++ repoze/bfg/tests/test_traversal.py | 31 +++++++++++++++++++++++++++++-- repoze/bfg/traversal.py | 23 ++++++++++++++++++++--- setup.py | 2 +- 8 files changed, 61 insertions(+), 9 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index c210d998e..2ae332b0c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,7 @@ +0.2.5 + + - Add ``model_url`` API. + 0.2.4 - Added url-based dispatch. diff --git a/docs/api/traversal.rst b/docs/api/traversal.rst index 41d66b2eb..eac63a271 100644 --- a/docs/api/traversal.rst +++ b/docs/api/traversal.rst @@ -7,4 +7,5 @@ .. autofunction:: find_interface + .. autofunction:: model_url diff --git a/docs/conf.py b/docs/conf.py index 979c6d9ac..8d19cf4db 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -51,9 +51,9 @@ copyright = '2008, Agendaless Consulting' # other places throughout the built documents. # # The short X.Y version. -version = '0.2' +version = '0.2.5' # The full version, including alpha/beta/rc tags. -release = '0.2' +release = '0.2.5' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: diff --git a/docs/glossary.rst b/docs/glossary.rst index 4e88b51d2..27326f539 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -74,7 +74,8 @@ Glossary Interface An attribute of an object that determines its type. Location - The path to an object in a model graph. + The path to an object in a model graph. See :ref:`location_aware` + for more information about how to make a model object *location-aware*. Security policy An object that provides a mechanism to check authorization using authentication data and a permission associated with a model. It diff --git a/docs/narr/models.rst b/docs/narr/models.rst index efbcd47b2..7df265657 100644 --- a/docs/narr/models.rst +++ b/docs/narr/models.rst @@ -58,6 +58,8 @@ instance nodes in the graph: implement a ``__getitem__``, or if they do, their ``__getitem__`` method must raise a ``KeyError``. +.. _location_aware: + Location-Aware Model Instances ------------------------------ diff --git a/repoze/bfg/tests/test_traversal.py b/repoze/bfg/tests/test_traversal.py index 5cc7551c0..1ae92dcf7 100644 --- a/repoze/bfg/tests/test_traversal.py +++ b/repoze/bfg/tests/test_traversal.py @@ -169,6 +169,33 @@ class FindInterfaceTests(unittest.TestCase): result = finder(baz, IFoo) self.assertEqual(result.__name__, 'root') +class ModelURLTests(unittest.TestCase): + def _getFUT(self): + from repoze.bfg.traversal import model_url + return model_url + + def test_it(self): + baz = DummyContext() + bar = DummyContext(baz) + foo = DummyContext(bar) + root = DummyContext(foo) + root.__parent__ = None + root.__name__ = None + foo.__parent__ = root + foo.__name__ = 'foo ' + bar.__parent__ = foo + bar.__name__ = 'bar' + baz.__parent__ = bar + baz.__name__ = 'baz' + request = DummyRequest() + model_url = self._getFUT() + request = DummyRequest() + result = model_url(baz, request, 'this/theotherthing', 'that') + + self.assertEqual( + result, + 'http://example.com:5432/foo%20/bar/baz/this/theotherthing/that') + class DummyContext(object): def __init__(self, next=None): self.next = next @@ -179,8 +206,8 @@ class DummyContext(object): return self.next class DummyRequest: - pass - + application_url = 'http://example.com:5432/' + class DummyTraverser: def __init__(self, context): self.context = context diff --git a/repoze/bfg/traversal.py b/repoze/bfg/traversal.py index d0e140375..c72c90e5e 100644 --- a/repoze/bfg/traversal.py +++ b/repoze/bfg/traversal.py @@ -1,4 +1,5 @@ import urllib +import urlparse from zope.interface import classProvides from zope.interface import implements @@ -66,9 +67,25 @@ class ModelGraphTraverser(object): return ob, name, path def find_interface(context, interface): - """ Return an object providing 'interface' anywhere in the parent - chain of 'context' or None if no object providing that interface - can be found in the parent chain """ + """ Return an object providing ``interface`` anywhere in the + parent chain of ``context`` or ``None`` if no object providing + that interface can be found in the parent chain""" for location in LocationIterator(context): if interface.providedBy(location): return location + +def model_url(model, request, *elements): + """ Return the absolute URL of the model object based on the + ``wsgi.url_scheme``, ``HTTP_HOST`` or ``SERVER_NAME`` in the + request, plus any ``SCRIPT_NAME``. Any positional passed in as + ``elements`` will be joined by slashes and appended to the + generated URL. The passed in elements are *not* URL-quoted. The + ``model`` passed in must be :term:`location`-aware.""" + rpath = [] + for location in LocationIterator(model): + if location.__name__: + rpath.append(urllib.quote(location.__name__)) + path = list(reversed(rpath)) + path.extend(elements) + path = '/'.join(path) + return urlparse.urljoin(request.application_url, path) diff --git a/setup.py b/setup.py index e0df62e4e..d1d324d68 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ # ############################################################################## -__version__ = '0.2.4' +__version__ = '0.2.5' import os -- cgit v1.2.3