diff options
| author | Michael Merickel <michael@merickel.org> | 2013-08-19 21:53:10 -0500 |
|---|---|---|
| committer | Michael Merickel <michael@merickel.org> | 2013-08-19 21:53:10 -0500 |
| commit | c14c8d7c86bd5cad207fab9acc053dc723dd2acf (patch) | |
| tree | 35dc7e54a4a739a0f5abf37f4e80c7f332c0b40c | |
| parent | 4aec433291dc7b0c08d27fe6352ecf7585052e73 (diff) | |
| parent | a6aa55360986b008b086551ea7414d3bf6e054e9 (diff) | |
| download | pyramid-c14c8d7c86bd5cad207fab9acc053dc723dd2acf.tar.gz pyramid-c14c8d7c86bd5cad207fab9acc053dc723dd2acf.tar.bz2 pyramid-c14c8d7c86bd5cad207fab9acc053dc723dd2acf.zip | |
Merge branch 'feature.issue611-external-static-routes' of tomster/pyramid into feature.external-routes
| -rw-r--r-- | CHANGES.txt | 4 | ||||
| -rw-r--r-- | docs/narr/urldispatch.rst | 39 | ||||
| -rw-r--r-- | pyramid/config/routes.py | 20 | ||||
| -rw-r--r-- | pyramid/tests/test_url.py | 51 |
4 files changed, 114 insertions, 0 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index b805a12a0..b4fe60085 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -142,6 +142,10 @@ Features - The ``alchemy`` scaffold tests now provide better coverage. See https://github.com/Pylons/pyramid/pull/1029 +- The ``pyramid.config.Configurator.add_route`` method now supports being called + with an external URL as pattern. See https://github.com/Pylons/pyramid/issues/611 + for more information. + Bug Fixes --------- diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index 310c160c0..8f03b1080 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -105,6 +105,7 @@ to using the previous combination of ``add_route`` and ``add_view``. .. _route_pattern_syntax: + Route Pattern Syntax ~~~~~~~~~~~~~~~~~~~~ @@ -127,6 +128,10 @@ and: /{foo}/bar/baz +If a pattern is a valid URL it won't be ever matched against an incoming +request. Instead it can be useful for generating external URLs. See +:ref:`External routes <external_route_narr>` for details. + A pattern segment (an individual item between ``/`` characters in the pattern) may either be a literal string (e.g. ``foo``) *or* it may be a replacement marker (e.g. ``{foo}``) or a certain combination of both. A @@ -754,9 +759,43 @@ other non-``name`` and non-``pattern`` arguments to exception to this rule is use of the ``pregenerator`` argument, which is not ignored when ``static`` is ``True``. +:ref:`External routes <external_route_narr>` are implicitly static. + .. versionadded:: 1.1 the ``static`` argument to :meth:`~pyramid.config.Configurator.add_route` +.. _external_route_narr: + + +External Routes +--------------- + +.. versionadded:: 1.5 + +Route patterns that are valid URLs, are treated as external routes. Like +:ref:`static routes <static_route_narr>` they are useful for URL generation +purposes only and are never considered for matching at request time. + +.. code-block:: python + :linenos: + + >>> config = Configurator() + >>> config.add_route('youtube', 'https://youtube.com/watch/{video_id}') + ... + >>> request.route_url('youtube', video_id='oHg5SJYRHA0') + >>> "https://youtube.com/watch/oHg5SJYRHA0" + +All pattern replacements and calls to +:meth:`pyramid.request.Request.route_url` will work as expected. Note that +:meth:`pyramid.request.Request.route_path` will also just return the external +URLs path part. + +.. note:: + + The external URL feature is implemented with a :term:`pregenerator` so you + cannot use both with the same route. + + .. index:: single: redirecting to slash-appended routes diff --git a/pyramid/config/routes.py b/pyramid/config/routes.py index c86e4a2dd..0e11428db 100644 --- a/pyramid/config/routes.py +++ b/pyramid/config/routes.py @@ -1,4 +1,5 @@ import warnings +from urlparse import urlparse from pyramid.interfaces import ( IRequest, @@ -387,6 +388,25 @@ class RoutesConfiguratorMixin(object): if self.route_prefix: pattern = self.route_prefix.rstrip('/') + '/' + pattern.lstrip('/') + if pregenerator is None: + parsed = urlparse(pattern) + if parsed.hostname: + pattern = parsed.path + + def external_url_pregenerator(request, elements, kw): + if not '_app_url' in kw: + if '_scheme' in kw and parsed.scheme != kw['_scheme']: + scheme = kw['_scheme'] + elif parsed.scheme: + scheme = parsed.scheme + else: + scheme = request.scheme + kw['_app_url'] = '{0}://{1}'.format( + scheme, parsed.netloc) + return elements, kw + + pregenerator = external_url_pregenerator + mapper = self.get_routes_mapper() introspectables = [] diff --git a/pyramid/tests/test_url.py b/pyramid/tests/test_url.py index bf1c514c5..0e006f447 100644 --- a/pyramid/tests/test_url.py +++ b/pyramid/tests/test_url.py @@ -1027,6 +1027,57 @@ class Test_current_route_path(unittest.TestCase): self.assertEqual(request.elements, ('abc',)) self.assertEqual(request.kw, {'_anchor':'abc'}) +class Test_external_static_url_integration(unittest.TestCase): + + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _makeRequest(self): + from pyramid.request import Request + return Request.blank('/') + + def test_generate_external_url(self): + self.config.add_route('acme', 'https://acme.org/path/{foo}') + request = self._makeRequest() + request.registry = self.config.registry + self.assertEqual(request.route_url('acme', foo='bar'), + 'https://acme.org/path/bar') + + def test_generate_external_url_without_scheme(self): + self.config.add_route('acme', '//acme.org/path/{foo}') + request = self._makeRequest() + request.registry = self.config.registry + self.assertEqual(request.route_url('acme', foo='bar'), + 'http://acme.org/path/bar') + + + def test_generate_external_url_with_explicit_scheme(self): + self.config.add_route('acme', '//acme.org/path/{foo}') + request = self._makeRequest() + request.registry = self.config.registry + self.assertEqual(request.route_url('acme', foo='bar', _scheme='https'), + 'https://acme.org/path/bar') + + + def test_generate_external_url_with_explicit_app_url(self): + self.config.add_route('acme', 'http://acme.org/path/{foo}') + request = self._makeRequest() + request.registry = self.config.registry + self.assertEqual(request.route_url('acme', foo='bar', _app_url='http://fakeme.com'), + 'http://fakeme.com/path/bar') + + + def test_generate_external_url_route_path(self): + self.config.add_route('acme', 'https://acme.org/path/{foo}') + request = self._makeRequest() + request.registry = self.config.registry + self.assertEqual(request.route_path('acme', foo='bar'), + '/path/bar') + + class DummyContext(object): def __init__(self, next=None): self.next = next |
