summaryrefslogtreecommitdiff
path: root/repoze
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2008-07-22 11:02:28 +0000
committerChris McDonough <chrism@agendaless.com>2008-07-22 11:02:28 +0000
commit8392d7094c96f777f6d1d5f0c62051702191d106 (patch)
tree6fd39e2c10fc33a5b2be08e7467c9e6ad0887b78 /repoze
parentb584143b2c408b979c340939f48b8a4ffe725e62 (diff)
downloadpyramid-8392d7094c96f777f6d1d5f0c62051702191d106.tar.gz
pyramid-8392d7094c96f777f6d1d5f0c62051702191d106.tar.bz2
pyramid-8392d7094c96f777f6d1d5f0c62051702191d106.zip
Add url-based dispatch.
Diffstat (limited to 'repoze')
-rw-r--r--repoze/bfg/interfaces.py3
-rw-r--r--repoze/bfg/tests/test_traversal.py4
-rw-r--r--repoze/bfg/tests/test_urldispatch.py85
-rw-r--r--repoze/bfg/urldispatch.py75
4 files changed, 165 insertions, 2 deletions
diff --git a/repoze/bfg/interfaces.py b/repoze/bfg/interfaces.py
index a239793ed..489fe0559 100644
--- a/repoze/bfg/interfaces.py
+++ b/repoze/bfg/interfaces.py
@@ -66,3 +66,6 @@ class IViewPermissionFactory(Interface):
def __call__(context, request):
""" Return an IViewPermission """
+class IURLDispatchModel(Interface):
+ """ A model that is created as a result of URL dispatching """
+
diff --git a/repoze/bfg/tests/test_traversal.py b/repoze/bfg/tests/test_traversal.py
index 590a06b81..5cc7551c0 100644
--- a/repoze/bfg/tests/test_traversal.py
+++ b/repoze/bfg/tests/test_traversal.py
@@ -48,12 +48,12 @@ class ModelGraphTraverserTests(unittest.TestCase, PlacelessSetup):
environ.update(kw)
return environ
- def test_class_conforms_to_IPublishTraverser(self):
+ def test_class_conforms_to_ITraverser(self):
from zope.interface.verify import verifyClass
from repoze.bfg.interfaces import ITraverser
verifyClass(ITraverser, self._getTargetClass())
- def test_instance_conforms_to_IPublishTraverser(self):
+ def test_instance_conforms_to_ITraverser(self):
from zope.interface.verify import verifyObject
from repoze.bfg.interfaces import ITraverser
context = DummyContext()
diff --git a/repoze/bfg/tests/test_urldispatch.py b/repoze/bfg/tests/test_urldispatch.py
new file mode 100644
index 000000000..a3e156861
--- /dev/null
+++ b/repoze/bfg/tests/test_urldispatch.py
@@ -0,0 +1,85 @@
+import unittest
+
+class RoutesMapperTests(unittest.TestCase):
+ def _getEnviron(self, **kw):
+ environ = {'SERVER_NAME':'localhost',
+ 'wsgi.url_scheme':'http'}
+ environ.update(kw)
+ return environ
+
+ def _getTargetClass(self):
+ from repoze.bfg.urldispatch import RoutesMapper
+ return RoutesMapper
+
+ def _makeOne(self, get_root):
+ klass = self._getTargetClass()
+ return klass(get_root)
+
+ def test_routes_mapper_no_route_matches(self):
+ marker = ()
+ get_root = make_get_root(marker)
+ mapper = self._makeOne(get_root)
+ environ = self._getEnviron(PATH_INFO='/')
+ result = mapper(environ)
+ self.assertEqual(result, marker)
+
+ def test_routes_mapper_route_matches(self):
+ marker = ()
+ get_root = make_get_root(marker)
+ mapper = self._makeOne(get_root)
+ mapper.connect('archives/:action/:article', controller='foo')
+ environ = self._getEnviron(PATH_INFO='/archives/action1/article1')
+ result = mapper(environ)
+ self.assertEqual(result.controller, 'foo')
+ self.assertEqual(result.action, 'action1')
+ self.assertEqual(result.article, 'article1')
+
+ def test_url_for(self):
+ marker = ()
+ get_root = make_get_root(marker)
+ mapper = self._makeOne(get_root)
+ mapper.connect('archives/:action/:article', controller='foo')
+ environ = self._getEnviron(PATH_INFO='/archives/action1/article1')
+ result = mapper(environ)
+ from routes import url_for
+ result = url_for(controller='foo', action='action2', article='article2')
+ self.assertEqual(result, '/archives/action2/article2')
+
+class TestRoutesModelTraverser(unittest.TestCase):
+ def _getTargetClass(self):
+ from repoze.bfg.urldispatch import RoutesModelTraverser
+ return RoutesModelTraverser
+
+ def _makeOne(self, model, request):
+ klass = self._getTargetClass()
+ return klass(model, request)
+
+ def test_class_conforms_to_ITraverser(self):
+ from zope.interface.verify import verifyClass
+ from repoze.bfg.interfaces import ITraverser
+ verifyClass(ITraverser, self._getTargetClass())
+
+ def test_instance_conforms_to_ITraverser(self):
+ from zope.interface.verify import verifyObject
+ from repoze.bfg.interfaces import ITraverser
+ verifyObject(ITraverser, self._makeOne(None, None))
+
+ def test_call(self):
+ model = DummyModel()
+ traverser = self._makeOne(model, None)
+ result = traverser({})
+ self.assertEqual(result[0], model)
+ self.assertEqual(result[1], 'controller')
+ self.assertEqual(result[2], '')
+
+class DummyModel:
+ controller = 'controller'
+
+def make_get_root(result):
+ def dummy_get_root(environ):
+ return result
+ return dummy_get_root
+
+
+
+
diff --git a/repoze/bfg/urldispatch.py b/repoze/bfg/urldispatch.py
new file mode 100644
index 000000000..9bdb1e5fa
--- /dev/null
+++ b/repoze/bfg/urldispatch.py
@@ -0,0 +1,75 @@
+from zope.interface import implements
+from zope.interface import classProvides
+
+from routes import Mapper
+from routes import request_config
+
+from repoze.bfg.interfaces import IURLDispatchModel
+from repoze.bfg.interfaces import ITraverserFactory
+from repoze.bfg.interfaces import ITraverser
+
+class RoutesModel(object):
+ implements(IURLDispatchModel)
+ def __init__(self, **kw):
+ self.__dict__.update(kw)
+
+class RoutesMapper(object):
+ """ The RoutesMapper is a wrapper for the ``get_root`` callable
+ passed in to the repoze.bfg Router at initialization time. When
+ it is instantiated, it wraps the get_root of an application in
+ such a way that the `Routes
+ <http://routes.groovie.org/index.html>`_ engine has the 'first
+ crack' at resolving the current request URL to a repoze.bfg view.
+ If the ``RoutesModelTraverser`` is configured in your
+ application's configure.zcml, any view that claims it is 'for' the
+ interface ``repoze.bfg.interfaces.IURLDispatchModel`` will be
+ called if its *name* matches the Routes 'controller' name for the
+ match. It will be passed a context object that has attributes
+ that match the Routes match arguments dictionary keys. If no
+ Routes route matches the current request, the 'fallback' get_root
+ is called."""
+ def __init__(self, get_root):
+ self.get_root = get_root
+ self.mapper = Mapper(controller_scan=None, directory=None,
+ explicit=True, always_scan=False)
+ self.mapper.explicit = True
+ self._regs_created = False
+
+ def __call__(self, environ):
+ if not self._regs_created:
+ self.mapper.create_regs([])
+ self._regs_created = True
+ path = environ.get('PATH_INFO', '/')
+ args = self.mapper.match(path)
+ if args:
+ config = request_config()
+ config.mapper = self.mapper
+ config.mapper_dict = args
+ config.host = environ.get('HTTP_HOST', environ['SERVER_NAME'])
+ config.protocol = environ['wsgi.url_scheme']
+ config.redirect = None
+ model = RoutesModel(**args)
+ return model
+ # fall back to original get_root
+ return self.get_root(environ)
+
+ def connect(self, *arg, **kw):
+ """ Add a route to the Routes mapper associated with this
+ request. This method accepts the same arguments as a Routes
+ *Mapper* object"""
+ self.mapper.connect(*arg, **kw)
+
+class RoutesModelTraverser(object):
+ classProvides(ITraverserFactory)
+ implements(ITraverser)
+ def __init__(self, model, request):
+ self.model = model
+ self.request = request
+
+ def __call__(self, environ):
+ return self.model, self.model.controller, ''
+
+
+
+
+