Commits

Andriy Kornatskyy committed 4dd1aec

Added exact match strategy processing to PathRouter.

Comments (0)

Files changed (3)

src/wheezy/routing/route.py

 class PlainRoute(object):
     """ Route based on string equalty operation.
     """
-    __slots__ = ('pattern', 'kwargs', 'matched', 'match')
+    __slots__ = ('pattern', 'kwargs', 'matched', 'match', 'exact_matches')
 
     def __init__(self, pattern, finishing, kwargs=None):
         """ Initializes the route by given ``pattern``. If
 
             >>> r = PlainRoute(r'abc', True)
             >>> assert r.match == r.equals_match
+            >>> r.exact_matches
+            (('abc', None),)
 
             Otherwise ``startswith_match`` strategy is selected.
 
         self.kwargs = kwargs
         self.matched = len(pattern)
         # Choose match strategy
-        self.match = finishing and self.equals_match \
-            or self.startswith_match
+        if finishing:
+            self.match = self.equals_match
+            self.exact_matches = ((pattern, kwargs), )
+        else:
+            self.match = self.startswith_match
+            self.exact_matches = None
 
     def equals_match(self, path):
         """ If the ``path`` exactly equals pattern string,

src/wheezy/routing/router.py

 class PathRouter(object):
     """
     """
-    __slots__ = ('mapping', 'route_map', 'inner_route_map', 'route_builders')
+    __slots__ = ('mapping', 'path_map', 'route_map', 'inner_route_map',
+                 'route_builders')
 
     def __init__(self, route_builders=None):
         """
         """
         self.mapping = []
         self.route_map = {}
+        self.path_map = {}
         self.inner_route_map = {}
         self.route_builders = route_builders or default_route_builders
 
         # build finishing route
         route = build_route(pattern, True, kwargs, self.route_builders)
         self.route_map[name] = route.path
-        self.mapping.append((route.match, handler))
+        if hasattr(route, 'exact_matches'):
+            for pattern, kwargs in route.exact_matches:
+                if pattern in self.path_map:
+                    warn('PathRouter: overriding path: %s.' % pattern)
+                self.path_map[pattern] = (handler, kwargs)
+        else:
+            self.mapping.append((route.match, handler))
 
     def include(self, pattern, included, kwargs=None):
         """ Includes nested routes below the current.
             >>> r.add_routes([
             ...     (r'login', Login)
             ... ])
-            >>> assert r.mapping
-            >>> assert r.route_map
+
+            >> assert r.mapping
+            >> assert r.route_map
 
             If ``handler`` is tuple, list or an instance of
             PathRouter than we proceed with ``include`` function
             >>> kwargs
             {}
         """
+        if path in self.path_map:
+            return self.path_map[path]
         for route_match, handler in self.mapping:
             matched, kwargs = route_match(path)
             if matched >= 0:

src/wheezy/routing/tests/test_router.py

         """
         self.r.add_route(r'abc', 'x', kwargs=None, name='n')
 
-        assert 1 == len(self.r.route_map) == len(self.r.mapping)
+        assert 1 == len(self.r.route_map) == len(self.r.path_map)
+        assert 0 == len(self.r.mapping)
         assert self.r.route_map['n']
 
 
         r = PathRouter()
         r.add_routes([('pattern', 'handler')])
         assert r.route_map
-        assert r.mapping
+        assert r.path_map
+        assert not r.mapping
 
     def test_mapping_is_tuple_of_three(self):
         """ ``mapping`` is a tuple of three elements.
         r = PathRouter()
         r.add_routes([('pattern', 'handler', {})])
         assert r.route_map
-        assert r.mapping
+        assert r.path_map
+        assert not r.mapping
 
     def test_mapping_is_tuple_of_four(self):
         """ ``mapping`` is a tuple of four elements.
         r = PathRouter()
         r.add_routes([('pattern', 'handler', {}, 'name')])
         assert r.route_map
-        assert r.mapping
+        assert r.path_map
+        assert not r.mapping
 
     def test_include(self):
         """ ``include`` call.
         assert handler is None
         assert kwargs == {}
 
-    def test_no_match_continue(self):
-        """ there is no match, continue with the rest
-            in ``self.mapping``.
-        """
-        expect(self.mock_inner.match('de')).result((None, None))
-        self.m.replay()
-
-        self.r.include('abc/', [])
-        self.r.add_route('abc/de', 'h')
-        handler, kwargs = self.r.match('abc/de')
-
-        self.assertEquals('h', handler)
-
     def test_no_kwargs(self):
         """ there is a match is inner, kwargs and kwargs2
             are None.