diff options
| author | Chris McDonough <chrism@agendaless.com> | 2010-09-08 04:25:35 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2010-09-08 04:25:35 +0000 |
| commit | 74409d12f7eb085bc992a200cc74799e4d1ff355 (patch) | |
| tree | 14b10948171be45b425f87122be156a7dc11c117 /repoze | |
| parent | 68469214646debcdcea662f34b41f41e0ae8db12 (diff) | |
| download | pyramid-74409d12f7eb085bc992a200cc74799e4d1ff355.tar.gz pyramid-74409d12f7eb085bc992a200cc74799e4d1ff355.tar.bz2 pyramid-74409d12f7eb085bc992a200cc74799e4d1ff355.zip | |
- The ``repoze.bfg.urldispatch.Route`` constructor (not an API) now
accepts a different ordering of arguments. Previously it was
``(pattern, name, factory=None, predicates=())``. It is now
``(name, pattern, factory=None, predicates=())``. This is in
support of consistency with ``configurator.add_route``.
- The ``repoze.bfg.urldispatch.RoutesMapper.connect`` method (not an
API) now accepts a different ordering of arguments. Previously it
was ``(pattern, name, factory=None, predicates=())``. It is now
``(name, pattern, factory=None, predicates=())``. This is in
support of consistency with ``configurator.add_route``.
- The ``repoze.bfg.urldispatch.RoutesMapper`` object now has a
``get_route`` method which returns a single Route object or
``None``.
- A new interface ``repoze.bfg.interfaces.IRoute`` was added. The
``repoze.bfg.urldispatch.Route`` object implements this interface.
- The canonical attribute for accessing the routing pattern from a
route object is now ``pattern`` rather than ``path``.
- The argument to ``repoze.bfg.configuration.Configurator.add_route``
which was previously called ``path`` is now called ``pattern`` for
better explicability. For backwards compatibility purposes, passing
a keyword argument named ``path`` to ``add_route`` will still work
indefinitely.
- The ``path`` attribute to the ZCML ``route`` directive is now named
``pattern`` for better explicability. The older ``path`` attribute
will continue to work indefinitely.
- All narrative, API, and tutorial docs which referred to a route
pattern as a ``path`` have now been updated to refer to them as a
``pattern``.
- The routesalchemy template has been updated to use ``pattern`` in
its route declarations rather than ``path``.
Diffstat (limited to 'repoze')
| -rw-r--r-- | repoze/bfg/configuration.py | 42 | ||||
| -rw-r--r-- | repoze/bfg/interfaces.py | 45 | ||||
| -rw-r--r-- | repoze/bfg/paster_templates/routesalchemy/+package+/configure.zcml | 2 | ||||
| -rw-r--r-- | repoze/bfg/testing.py | 8 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_configuration.py | 11 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_router.py | 24 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_urldispatch.py | 69 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_zcml.py | 62 | ||||
| -rw-r--r-- | repoze/bfg/urldispatch.py | 21 | ||||
| -rw-r--r-- | repoze/bfg/zcml.py | 17 |
10 files changed, 221 insertions, 80 deletions
diff --git a/repoze/bfg/configuration.py b/repoze/bfg/configuration.py index 488761ec4..962ba8f25 100644 --- a/repoze/bfg/configuration.py +++ b/repoze/bfg/configuration.py @@ -1005,7 +1005,7 @@ class Configurator(object): def add_route(self, name, - path, + pattern=None, view=None, view_for=None, permission=None, @@ -1025,6 +1025,7 @@ class Configurator(object): view_context=None, view_attr=None, use_global_views=False, + path=None, _info=u''): """ Add a :term:`route configuration` to the current configuration state, as well as possibly a :term:`view @@ -1064,7 +1065,7 @@ class Configurator(object): route). The syntax of the ``traverse`` argument is the same as it is - for ``path``. For example, if the ``path`` provided to + for ``pattern``. For example, if the ``pattern`` provided to ``add_route`` is ``articles/:article/edit``, and the ``traverse`` argument provided to ``add_route`` is ``/:article``, when a request comes in that causes the route @@ -1078,14 +1079,15 @@ class Configurator(object): traversal. If the traversal path contains segment marker names which - are not present in the path argument, a runtime error will - occur. The ``traverse`` pattern should not contain segment - markers that do not exist in the ``path``. + are not present in the ``pattern`` argument, a runtime error + will occur. The ``traverse`` pattern should not contain + segment markers that do not exist in the ``pattern`` + argument. A similar combining of routing and traversal is available when a route is matched which contains a ``*traverse`` - remainder marker in its path (see - :ref:`using_traverse_in_a_route_path`). The ``traverse`` + remainder marker in its pattern (see + :ref:`using_traverse_in_a_route_pattern`). The ``traverse`` argument to add_route allows you to associate route patterns with an arbitrary traversal path without using a a ``*traverse`` remainder marker; instead you can use other @@ -1093,18 +1095,26 @@ class Configurator(object): Note that the ``traverse`` argument to ``add_route`` is ignored when attached to a route that has a ``*traverse`` - remainder marker in its path. + remainder marker in its pattern. .. note:: This feature is new as of :mod:`repoze.bfg` 1.3. Predicate Arguments - path + pattern - The path of the route e.g. ``ideas/:idea``. This argument - is required. See :ref:`route_path_pattern_syntax` for - information about the syntax of route paths. If the path - doesn't match the current URL, route matching continues. + The pattern of the route e.g. ``ideas/:idea``. This + argument is required. See :ref:`route_path_pattern_syntax` + for information about the syntax of route patterns. If the + pattern doesn't match the current URL, route matching + continues. + + .. note:: For backwards compatibility purposes (as of + :mod:`repoze.bfg` 1.3), a ``path`` keyword argument + passed to this function will be used to represent the + pattern value if the ``pattern`` argument is ``None``. + If both ``path`` and ``pattern`` are passed, ``pattern`` + wins. xhr @@ -1333,7 +1343,11 @@ class Configurator(object): mapper = RoutesMapper() self.registry.registerUtility(mapper, IRoutesMapper) factory = self.maybe_dotted(factory) - return mapper.connect(path, name, factory, predicates=predicates) + if pattern is None: + pattern = path + if pattern is None: + raise ConfigurationError('"pattern" argument may not be None') + return mapper.connect(name, pattern, factory, predicates=predicates) def scan(self, package=None, categories=None, _info=u''): """ Scan a Python package and any of its subpackages for diff --git a/repoze/bfg/interfaces.py b/repoze/bfg/interfaces.py index 86e2206e3..7082846e4 100644 --- a/repoze/bfg/interfaces.py +++ b/repoze/bfg/interfaces.py @@ -234,12 +234,48 @@ class IDebugLogger(Interface): ILogger = IDebugLogger # b/c +class IRoute(Interface): + """ Interface representing the type of object returned from + ``IRoutesMapper.get_route``""" + name = Attribute('The route name') + pattern = Attribute('The route pattern') + factory = Attribute( + 'The :term:`root factory` used by the :mod:`repoze.bfg` router ' + 'when this route matches (or ``None``)') + predicates = Attribute( + 'A sequence of :term:`route predicate` objects used to ' + 'determine if a request matches this route or not or not after ' + 'basic pattern matching has been completed.') + def match(path): + """ + If the ``path`` passed to this function can be matched by the + ``pattern`` of this route, return a dictionary (the + 'matchdict'), which will contain keys representing the dynamic + segment markers in the pattern mapped to values extracted from + the provided ``path``. + + If the ``path`` passed to this function cannot be matched by + the ``pattern`` of this route, return ``None``. + """ + def generate(kw): + """ + Generate a URL based on filling in the dynamic segment markers + in the pattern using the ``kw`` dictionary provided. + """ + class IRoutesMapper(Interface): """ Interface representing a Routes ``Mapper`` object """ def get_routes(): """ Return a sequence of Route objects registered in the mapper.""" - def connect(path, name, factory=None, predicates=()): + def has_routes(): + """ Returns ``True`` if any route has been registered. """ + + def get_route(name): + """ Returns an ``IRoute`` object if a route with the name ``name`` + was registered, otherwise return ``None``.""" + + def connect(name, pattern, factory=None, predicates=()): """ Add a new route. """ def generate(name, kw): @@ -247,8 +283,11 @@ class IRoutesMapper(Interface): keywords implied by kw""" def __call__(request): - """ Return a matchdict for the request; the ``route`` key will - either be a Route object or ``None`` if no route matched.""" + """ Return a dictionary containing matching information for + the request; the ``route`` key of this dictionary will either + be a Route object or ``None`` if no route matched; the + ``match``key will be the matchdict or ``None`` if no route + matched.""" class IContextURL(Interface): """ An adapter which deals with URLs related to a context. diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/configure.zcml b/repoze/bfg/paster_templates/routesalchemy/+package+/configure.zcml index 1a8d3e0bf..6d16bd089 100644 --- a/repoze/bfg/paster_templates/routesalchemy/+package+/configure.zcml +++ b/repoze/bfg/paster_templates/routesalchemy/+package+/configure.zcml @@ -4,7 +4,7 @@ <include package="repoze.bfg.includes" /> <route - path="" + pattern="" name="home" view=".views.my_view" view_renderer="templates/mytemplate.pt" diff --git a/repoze/bfg/testing.py b/repoze/bfg/testing.py index 5502cb3d2..c99ba8706 100644 --- a/repoze/bfg/testing.py +++ b/repoze/bfg/testing.py @@ -290,12 +290,12 @@ def registerSubscriber(subscriber, iface=Interface): config = Configurator(registry) return config.add_subscriber(subscriber, iface=iface) -def registerRoute(path, name, factory=None): - """ Register a new :term:`route` using a path +def registerRoute(pattern, name, factory=None): + """ Register a new :term:`route` using a pattern (e.g. ``:pagename``), a name (e.g. ``home``), and an optional root factory. - The ``path`` argument implies the route path. The ``name`` + The ``pattern`` argument implies the route pattern. The ``name`` argument implies the route name. The ``factory`` argument implies a :term:`root factory` associated with the route. @@ -311,7 +311,7 @@ def registerRoute(path, name, factory=None): """ reg = get_current_registry() config = Configurator(registry=reg) - return config.add_route(name, path, factory=factory) + return config.add_route(name, pattern, factory=factory) def registerRoutesMapper(root_factory=None): """ Register a routes 'mapper' object. diff --git a/repoze/bfg/tests/test_configuration.py b/repoze/bfg/tests/test_configuration.py index 1ee2c1018..fc01e3ed4 100644 --- a/repoze/bfg/tests/test_configuration.py +++ b/repoze/bfg/tests/test_configuration.py @@ -1936,6 +1936,17 @@ class ConfiguratorTests(unittest.TestCase): self._assertRoute(config, 'name', 'path') self.failUnless(hasattr(wrapper, '__call_permissive__')) + def test_add_route_no_pattern_with_path(self): + config = self._makeOne() + route = config.add_route('name', path='path') + self._assertRoute(config, 'name', 'path') + self.assertEqual(route.name, 'name') + + def test_add_route_no_path_no_pattern(self): + from repoze.bfg.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.add_route, 'name') + def test__override_not_yet_registered(self): from repoze.bfg.interfaces import IPackageOverrides package = DummyPackage('package') diff --git a/repoze/bfg/tests/test_router.py b/repoze/bfg/tests/test_router.py index 1ce499e41..c243d739e 100644 --- a/repoze/bfg/tests/test_router.py +++ b/repoze/bfg/tests/test_router.py @@ -18,14 +18,14 @@ class TestRouter(unittest.TestCase): self.registry.registerUtility(iface, IRouteRequest, name=name) return iface - def _connectRoute(self, path, name, factory=None): + def _connectRoute(self, name, path, factory=None): from repoze.bfg.interfaces import IRoutesMapper from repoze.bfg.urldispatch import RoutesMapper mapper = self.registry.queryUtility(IRoutesMapper) if mapper is None: mapper = RoutesMapper() self.registry.registerUtility(mapper, IRoutesMapper) - mapper.connect(path, name, factory) + mapper.connect(name, path, factory) def _registerLogger(self): from repoze.bfg.interfaces import IDebugLogger @@ -444,7 +444,7 @@ class TestRouter(unittest.TestCase): root = object() def factory(request): return root - self._connectRoute('archives/:action/:article', 'foo', factory) + self._connectRoute('foo', 'archives/:action/:article', factory) context = DummyContext() self._registerTraverserFactory(context) response = DummyResponse() @@ -485,7 +485,7 @@ class TestRouter(unittest.TestCase): root = object() def factory(request): return root - self._connectRoute('archives/:action/:article', 'foo', factory) + self._connectRoute('foo', 'archives/:action/:article', factory) context = DummyContext() self._registerTraverserFactory(context) response = DummyResponse() @@ -727,7 +727,7 @@ class TestRouter(unittest.TestCase): from repoze.bfg.interfaces import IViewClassifier from repoze.bfg.interfaces import IExceptionViewClassifier req_iface = self._registerRouteRequest('foo') - self._connectRoute('archives/:action/:article', 'foo', None) + self._connectRoute('foo', 'archives/:action/:article', None) view = DummyView(DummyResponse(), raise_exception=RuntimeError) self._registerView(view, '', IViewClassifier, req_iface, None) response = DummyResponse() @@ -746,7 +746,7 @@ class TestRouter(unittest.TestCase): from repoze.bfg.interfaces import IExceptionViewClassifier from repoze.bfg.interfaces import IRequest req_iface = self._registerRouteRequest('foo') - self._connectRoute('archives/:action/:article', 'foo', None) + self._connectRoute('foo', 'archives/:action/:article', None) view = DummyView(DummyResponse(), raise_exception=RuntimeError) self._registerView(view, '', IViewClassifier, IRequest, None) response = DummyResponse() @@ -764,7 +764,7 @@ class TestRouter(unittest.TestCase): from repoze.bfg.interfaces import IExceptionViewClassifier from repoze.bfg.interfaces import IRequest req_iface = self._registerRouteRequest('foo') - self._connectRoute('archives/:action/:article', 'foo', None) + self._connectRoute('foo', 'archives/:action/:article', None) view = DummyView(DummyResponse(), raise_exception=RuntimeError) self._registerView(view, '', IViewClassifier, req_iface, None) response = DummyResponse() @@ -787,7 +787,7 @@ class TestRouter(unittest.TestCase): class SubException(SuperException): pass req_iface = self._registerRouteRequest('foo') - self._connectRoute('archives/:action/:article', 'foo', None) + self._connectRoute('foo', 'archives/:action/:article', None) view = DummyView(DummyResponse(), raise_exception=SuperException) self._registerView(view, '', IViewClassifier, req_iface, None) response = DummyResponse() @@ -809,7 +809,7 @@ class TestRouter(unittest.TestCase): class SubException(SuperException): pass req_iface = self._registerRouteRequest('foo') - self._connectRoute('archives/:action/:article', 'foo', None) + self._connectRoute('foo', 'archives/:action/:article', None) view = DummyView(DummyResponse(), raise_exception=SubException) self._registerView(view, '', IViewClassifier, req_iface, None) response = DummyResponse() @@ -832,7 +832,7 @@ class TestRouter(unittest.TestCase): class AnotherException(Exception): pass req_iface = self._registerRouteRequest('foo') - self._connectRoute('archives/:action/:article', 'foo', None) + self._connectRoute('foo', 'archives/:action/:article', None) view = DummyView(DummyResponse(), raise_exception=MyException) self._registerView(view, '', IViewClassifier, req_iface, None) response = DummyResponse() @@ -850,7 +850,7 @@ class TestRouter(unittest.TestCase): from repoze.bfg.interfaces import IExceptionViewClassifier from repoze.bfg.interfaces import IRequest req_iface = self._registerRouteRequest('foo') - self._connectRoute('archives/:action/:article', 'foo', None) + self._connectRoute('foo', 'archives/:action/:article', None) view = DummyView(DummyResponse(), raise_exception=RuntimeError) self._registerView(view, '', IViewClassifier, req_iface, None) response = DummyResponse() @@ -874,7 +874,7 @@ class TestRouter(unittest.TestCase): from repoze.bfg.interfaces import IExceptionViewClassifier req_iface = self._registerRouteRequest('foo') another_req_iface = self._registerRouteRequest('bar') - self._connectRoute('archives/:action/:article', 'foo', None) + self._connectRoute('foo', 'archives/:action/:article', None) view = DummyView(DummyResponse(), raise_exception=RuntimeError) self._registerView(view, '', IViewClassifier, req_iface, None) response = DummyResponse() diff --git a/repoze/bfg/tests/test_urldispatch.py b/repoze/bfg/tests/test_urldispatch.py index f51bf7e87..bf43750ea 100644 --- a/repoze/bfg/tests/test_urldispatch.py +++ b/repoze/bfg/tests/test_urldispatch.py @@ -9,9 +9,15 @@ class TestRoute(unittest.TestCase): def _makeOne(self, *arg): return self._getTargetClass()(*arg) + def test_provides_IRoute(self): + from repoze.bfg.interfaces import IRoute + from zope.interface.verify import verifyObject + verifyObject(IRoute, self._makeOne('name', 'pattern')) + def test_ctor(self): import types - route = self._makeOne(':path', 'name', 'factory') + route = self._makeOne('name', ':path', 'factory') + self.assertEqual(route.pattern, ':path') self.assertEqual(route.path, ':path') self.assertEqual(route.name, 'name') self.assertEqual(route.factory, 'factory') @@ -20,19 +26,20 @@ class TestRoute(unittest.TestCase): def test_ctor_defaults(self): import types - route = self._makeOne(':path') + route = self._makeOne('name', ':path') + self.assertEqual(route.pattern, ':path') self.assertEqual(route.path, ':path') - self.assertEqual(route.name, None) + self.assertEqual(route.name, 'name') self.assertEqual(route.factory, None) self.failUnless(route.generate.__class__ is types.FunctionType) self.failUnless(route.match.__class__ is types.FunctionType) def test_match(self): - route = self._makeOne(':path') + route = self._makeOne('name', ':path') self.assertEqual(route.match('/whatever'), {'path':'whatever'}) def test_generate(self): - route = self._makeOne(':path') + route = self._makeOne('name', ':path') self.assertEqual(route.generate({'path':'abc'}), '/abc') class RoutesMapperTests(unittest.TestCase): @@ -60,6 +67,11 @@ class RoutesMapperTests(unittest.TestCase): klass = self._getTargetClass() return klass() + def test_provides_IRoutesMapper(self): + from repoze.bfg.interfaces import IRoutesMapper + from zope.interface.verify import verifyObject + verifyObject(IRoutesMapper, self._makeOne()) + def test_no_route_matches(self): mapper = self._makeOne() request = self._getRequest(PATH_INFO='/') @@ -69,18 +81,18 @@ class RoutesMapperTests(unittest.TestCase): def test_connect_name_exists_removes_old(self): mapper = self._makeOne() - mapper.connect('archives/:action/:article', 'foo') - mapper.connect('archives/:action/:article2', 'foo') + mapper.connect('foo', 'archives/:action/:article') + mapper.connect('foo', 'archives/:action/:article2') self.assertEqual(len(mapper.routelist), 1) self.assertEqual(len(mapper.routes), 1) - self.assertEqual(mapper.routes['foo'].path, + self.assertEqual(mapper.routes['foo'].pattern, 'archives/:action/:article2') - self.assertEqual(mapper.routelist[0].path, + self.assertEqual(mapper.routelist[0].pattern, 'archives/:action/:article2') def test___call__route_matches(self): mapper = self._makeOne() - mapper.connect('archives/:action/:article', 'foo') + mapper.connect('foo', 'archives/:action/:article') request = self._getRequest(PATH_INFO='/archives/action1/article1') result = mapper(request) self.assertEqual(result['route'], mapper.routes['foo']) @@ -89,7 +101,7 @@ class RoutesMapperTests(unittest.TestCase): def test___call__route_matches_with_predicates(self): mapper = self._makeOne() - mapper.connect('archives/:action/:article', 'foo', + mapper.connect('foo', 'archives/:action/:article', predicates=[lambda *arg: True]) request = self._getRequest(PATH_INFO='/archives/action1/article1') result = mapper(request) @@ -99,9 +111,9 @@ class RoutesMapperTests(unittest.TestCase): def test___call__route_fails_to_match_with_predicates(self): mapper = self._makeOne() - mapper.connect('archives/:action/article1', 'foo', + mapper.connect('foo', 'archives/:action/article1', predicates=[lambda *arg: True, lambda *arg: False]) - mapper.connect('archives/:action/:article', 'bar') + mapper.connect('bar', 'archives/:action/:article') request = self._getRequest(PATH_INFO='/archives/action1/article1') result = mapper(request) self.assertEqual(result['route'], mapper.routes['bar']) @@ -114,7 +126,7 @@ class RoutesMapperTests(unittest.TestCase): self.assertEqual(info['match'], {'action':u'action1'}) self.assertEqual(info['route'], mapper.routes['foo']) return True - mapper.connect('archives/:action/article1', 'foo', predicates=[pred]) + mapper.connect('foo', 'archives/:action/article1', predicates=[pred]) request = self._getRequest(PATH_INFO='/archives/action1/article1') mapper(request) @@ -122,9 +134,9 @@ class RoutesMapperTests(unittest.TestCase): # "unordered" as reported in IRC by author of # http://labs.creativecommons.org/2010/01/13/cc-engine-and-web-non-frameworks/ mapper = self._makeOne() - mapper.connect('licenses/:license_code/:license_version/rdf', 'rdf') - mapper.connect('licenses/:license_code/:license_version/:jurisdiction', - 'juri') + mapper.connect('rdf', 'licenses/:license_code/:license_version/rdf') + mapper.connect('juri', + 'licenses/:license_code/:license_version/:jurisdiction') request = self._getRequest(PATH_INFO='/licenses/1/v2/rdf') result = mapper(request) @@ -141,7 +153,7 @@ class RoutesMapperTests(unittest.TestCase): def test___call__root_route_matches(self): mapper = self._makeOne() - mapper.connect('', 'root') + mapper.connect('root', '') request = self._getRequest(PATH_INFO='/') result = mapper(request) self.assertEqual(result['route'], mapper.routes['root']) @@ -149,7 +161,7 @@ class RoutesMapperTests(unittest.TestCase): def test___call__root_route_matches2(self): mapper = self._makeOne() - mapper.connect('/', 'root') + mapper.connect('root', '/') request = self._getRequest(PATH_INFO='/') result = mapper(request) self.assertEqual(result['route'], mapper.routes['root']) @@ -157,7 +169,7 @@ class RoutesMapperTests(unittest.TestCase): def test___call__root_route_when_path_info_empty(self): mapper = self._makeOne() - mapper.connect('/', 'root') + mapper.connect('root', '/') request = self._getRequest(PATH_INFO='') result = mapper(request) self.assertEqual(result['route'], mapper.routes['root']) @@ -165,7 +177,7 @@ class RoutesMapperTests(unittest.TestCase): def test___call__no_path_info(self): mapper = self._makeOne() - mapper.connect('/', 'root') + mapper.connect('root', '/') request = self._getRequest() result = mapper(request) self.assertEqual(result['route'], mapper.routes['root']) @@ -186,6 +198,17 @@ class RoutesMapperTests(unittest.TestCase): self.assertEqual(len(routes), 1) self.assertEqual(routes[0].__class__, Route) + def test_get_route_matches(self): + mapper = self._makeOne() + mapper.connect('whatever', 'archives/:action/:article') + result = mapper.get_route('whatever') + self.assertEqual(result.pattern, 'archives/:action/:article') + + def test_get_route_misses(self): + mapper = self._makeOne() + result = mapper.get_route('whatever') + self.assertEqual(result, None) + def test_generate(self): mapper = self._makeOne() def generator(kw): @@ -195,9 +218,9 @@ class RoutesMapperTests(unittest.TestCase): self.assertEqual(mapper.generate('abc', {}), 123) class TestCompileRoute(unittest.TestCase): - def _callFUT(self, path): + def _callFUT(self, pattern): from repoze.bfg.urldispatch import _compile_route - return _compile_route(path) + return _compile_route(pattern) def test_no_star(self): matcher, generator = self._callFUT('/foo/:baz/biz/:buz/bar') diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py index a01b52840..4cd7f88d3 100644 --- a/repoze/bfg/tests/test_zcml.py +++ b/repoze/bfg/tests/test_zcml.py @@ -514,7 +514,7 @@ class TestRouteDirective(unittest.TestCase): from repoze.bfg.zcml import route return route(*arg, **kw) - def _assertRoute(self, name, path, num_predicates=0): + def _assertRoute(self, name, pattern, num_predicates=0): from repoze.bfg.threadlocal import get_current_registry from repoze.bfg.interfaces import IRoutesMapper reg = get_current_registry() @@ -523,7 +523,7 @@ class TestRouteDirective(unittest.TestCase): route = routes[0] self.assertEqual(len(routes), 1) self.assertEqual(route.name, name) - self.assertEqual(route.path, path) + self.assertEqual(route.pattern, pattern) self.assertEqual(len(routes[0].predicates), num_predicates) return route @@ -535,7 +535,7 @@ class TestRouteDirective(unittest.TestCase): from repoze.bfg.interfaces import IRouteRequest context = DummyContext() view = lambda *arg: 'OK' - self._callFUT(context, 'name', 'path', view=view) + self._callFUT(context, 'name', 'pattern', view=view) actions = context.actions self.assertEqual(len(actions), 2) @@ -544,7 +544,7 @@ class TestRouteDirective(unittest.TestCase): route_discriminator = route_action['discriminator'] self.assertEqual(route_discriminator, ('route', 'name', False, None, None, None, None,None)) - self._assertRoute('name', 'path') + self._assertRoute('name', 'pattern') view_action = actions[1] reg = get_current_registry() @@ -563,7 +563,8 @@ class TestRouteDirective(unittest.TestCase): from repoze.bfg.interfaces import IRouteRequest context = DummyContext() view = lambda *arg: 'OK' - self._callFUT(context, 'name', 'path', view=view, view_context=IDummy) + self._callFUT(context, 'name', 'pattern', view=view, + view_context=IDummy) actions = context.actions self.assertEqual(len(actions), 2) @@ -572,7 +573,7 @@ class TestRouteDirective(unittest.TestCase): route_discriminator = route_action['discriminator'] self.assertEqual(route_discriminator, ('route', 'name', False, None, None, None, None,None)) - self._assertRoute('name', 'path') + self._assertRoute('name', 'pattern') view_action = actions[1] reg = get_current_registry() @@ -593,8 +594,8 @@ class TestRouteDirective(unittest.TestCase): view = lambda *arg: 'OK' class Foo: pass - self._callFUT(context, 'name', 'path', view=view, view_context=IDummy, - view_for=Foo) + self._callFUT(context, 'name', 'pattern', view=view, + view_context=IDummy, view_for=Foo) actions = context.actions self.assertEqual(len(actions), 2) @@ -603,7 +604,7 @@ class TestRouteDirective(unittest.TestCase): route_discriminator = route_action['discriminator'] self.assertEqual(route_discriminator, ('route', 'name', False, None, None, None, None,None)) - self._assertRoute('name', 'path') + self._assertRoute('name', 'pattern') view_action = actions[1] reg = get_current_registry() @@ -630,7 +631,7 @@ class TestRouteDirective(unittest.TestCase): context = DummyContext() view = lambda *arg: 'OK' - self._callFUT(context, 'name', 'path', view=view, + self._callFUT(context, 'name', 'pattern', view=view, renderer='fixtureapp/templates/foo.pt') actions = context.actions self.assertEqual(len(actions), 2) @@ -640,7 +641,7 @@ class TestRouteDirective(unittest.TestCase): route_discriminator = route_action['discriminator'] self.assertEqual(route_discriminator, ('route', 'name', False, None, None, None, None,None)) - self._assertRoute('name', 'path') + self._assertRoute('name', 'pattern') view_action = actions[1] request_type = reg.getUtility(IRouteRequest, 'name') @@ -660,7 +661,8 @@ class TestRouteDirective(unittest.TestCase): preds = tuple(sorted([pred1, pred2])) context = DummyContext() - self._callFUT(context, 'name', 'path', custom_predicates=(pred1, pred2)) + self._callFUT(context, 'name', 'pattern', + custom_predicates=(pred1, pred2)) actions = context.actions self.assertEqual(len(actions), 1) @@ -670,7 +672,39 @@ class TestRouteDirective(unittest.TestCase): self.assertEqual( route_discriminator, ('route', 'name', False, None, None, None, None,None) + preds) - self._assertRoute('name', 'path', 2) + self._assertRoute('name', 'pattern', 2) + + def test_with_path_argument_no_pattern(self): + context = DummyContext() + self._callFUT(context, 'name', path='pattern') + actions = context.actions + self.assertEqual(len(actions), 1) + + route_action = actions[0] + route_action['callable']() + route_discriminator = route_action['discriminator'] + self.assertEqual(route_discriminator, + ('route', 'name', False, None, None, None, None,None)) + self._assertRoute('name', 'pattern') + + def test_with_path_argument_and_pattern(self): + context = DummyContext() + self._callFUT(context, 'name', pattern='pattern', path='path') + actions = context.actions + self.assertEqual(len(actions), 1) + + route_action = actions[0] + route_action['callable']() + route_discriminator = route_action['discriminator'] + self.assertEqual(route_discriminator, + ('route', 'name', False, None, None, None, None,None)) + self._assertRoute('name', 'pattern') + + + def test_with_neither_path_nor_pattern(self): + from repoze.bfg.exceptions import ConfigurationError + context = DummyContext() + self.assertRaises(ConfigurationError, self._callFUT, context, 'name') class TestStaticDirective(unittest.TestCase): def setUp(self): @@ -706,7 +740,7 @@ class TestStaticDirective(unittest.TestCase): mapper = reg.getUtility(IRoutesMapper) routes = mapper.get_routes() self.assertEqual(len(routes), 1) - self.assertEqual(routes[0].path, 'name/*subpath') + self.assertEqual(routes[0].pattern, 'name/*subpath') self.assertEqual(routes[0].name, 'name/') view_action = actions[1] diff --git a/repoze/bfg/urldispatch.py b/repoze/bfg/urldispatch.py index a07f902f6..f9def62df 100644 --- a/repoze/bfg/urldispatch.py +++ b/repoze/bfg/urldispatch.py @@ -1,5 +1,9 @@ import re from urllib import unquote +from zope.interface import implements + +from repoze.bfg.interfaces import IRoutesMapper +from repoze.bfg.interfaces import IRoute from repoze.bfg.compat import all from repoze.bfg.encode import url_quote @@ -7,17 +11,21 @@ from repoze.bfg.exceptions import URLDecodeError from repoze.bfg.traversal import traversal_path from repoze.bfg.traversal import quote_path_segment + _marker = object() class Route(object): - def __init__(self, path, name=None, factory=None, predicates=()): - self.path = path - self.match, self.generate = _compile_route(path) + implements(IRoute) + def __init__(self, name, pattern, factory=None, predicates=()): + self.pattern = pattern + self.path = pattern # indefinite b/w compat, not in interface + self.match, self.generate = _compile_route(pattern) self.name = name self.factory = factory self.predicates = predicates class RoutesMapper(object): + implements(IRoutesMapper) def __init__(self): self.routelist = [] self.routes = {} @@ -28,11 +36,14 @@ class RoutesMapper(object): def get_routes(self): return self.routelist - def connect(self, path, name, factory=None, predicates=()): + def get_route(self, name): + return self.routes.get(name) + + def connect(self, name, pattern, factory=None, predicates=()): if name in self.routes: oldroute = self.routes[name] self.routelist.remove(oldroute) - route = Route(path, name, factory, predicates) + route = Route(name, pattern, factory, predicates) self.routelist.append(route) self.routes[name] = route return route diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py index 820dc9442..5320554bc 100644 --- a/repoze/bfg/zcml.py +++ b/repoze/bfg/zcml.py @@ -221,7 +221,9 @@ class IRouteDirective(Interface): """ The interface for the ``route`` ZCML directive """ name = TextLine(title=u'name', required=True) - path = TextLine(title=u'path', required=True) + pattern = TextLine(title=u'pattern', required=False) + # alias for pattern + path = TextLine(title=u'path', required=False) factory = GlobalObject(title=u'context factory', required=False) view = GlobalObject(title=u'view', required=False) @@ -263,7 +265,7 @@ class IRouteDirective(Interface): def route(_context, name, - path, + pattern=None, view=None, view_for=None, permission=None, @@ -282,7 +284,8 @@ def route(_context, view_renderer=None, view_context=None, traverse=None, - use_global_views=False): + use_global_views=False, + path=None): """ Handle ``route`` ZCML directives """ # the strange ordering of the request kw args above is for b/w @@ -300,11 +303,17 @@ def route(_context, if view_renderer and '.' in view_renderer: view_renderer = path_spec(_context, view_renderer) + if pattern is None: + pattern = path + + if pattern is None: + raise ConfigurationError('route directive must include a "pattern"') + def register(): config = Configurator(reg, package=_context.package) config.add_route( name, - path, + pattern, factory=factory, header=header, xhr=xhr, |
