[Checkins] SVN: Sandbox/wichert/z3c.routes/ Experiment from grokkerdam

Wichert Akkerman wichert at wiggy.net
Sat May 3 18:47:06 EDT 2008


Log message for revision 86311:
  Experiment from grokkerdam

Changed:
  A   Sandbox/wichert/z3c.routes/
  A   Sandbox/wichert/z3c.routes/branches/
  A   Sandbox/wichert/z3c.routes/tags/
  A   Sandbox/wichert/z3c.routes/trunk/
  A   Sandbox/wichert/z3c.routes/trunk/README.txt
  A   Sandbox/wichert/z3c.routes/trunk/docs/
  A   Sandbox/wichert/z3c.routes/trunk/docs/HISTORY.txt
  A   Sandbox/wichert/z3c.routes/trunk/docs/LICENSE.txt
  A   Sandbox/wichert/z3c.routes/trunk/setup.cfg
  A   Sandbox/wichert/z3c.routes/trunk/setup.py
  A   Sandbox/wichert/z3c.routes/trunk/z3c/
  A   Sandbox/wichert/z3c.routes/trunk/z3c/__init__.py
  A   Sandbox/wichert/z3c.routes/trunk/z3c/routes/
  A   Sandbox/wichert/z3c.routes/trunk/z3c/routes/.traverser.py.swp
  A   Sandbox/wichert/z3c.routes/trunk/z3c/routes/__init__.py
  A   Sandbox/wichert/z3c.routes/trunk/z3c/routes/interfaces.py
  A   Sandbox/wichert/z3c.routes/trunk/z3c/routes/route.py
  A   Sandbox/wichert/z3c.routes/trunk/z3c/routes/tests/
  A   Sandbox/wichert/z3c.routes/trunk/z3c/routes/tests/__init__.py
  A   Sandbox/wichert/z3c.routes/trunk/z3c/routes/tests/testRoute.py
  A   Sandbox/wichert/z3c.routes/trunk/z3c/routes/tests/testTraverse.py
  A   Sandbox/wichert/z3c.routes/trunk/z3c/routes/traverser.py

-=-
Added: Sandbox/wichert/z3c.routes/trunk/README.txt
===================================================================
--- Sandbox/wichert/z3c.routes/trunk/README.txt	                        (rev 0)
+++ Sandbox/wichert/z3c.routes/trunk/README.txt	2008-05-03 22:47:05 UTC (rev 86311)
@@ -0,0 +1,41 @@
+Introduction
+============
+
+z3c.routes tries to bring routes_ to Zope, allowing you to use a mixture
+of Zope traversal and routes for publication traversal.
+
+Zope will normally use traversal to determine which object to publish.
+z3c.routes makes it possible to assign a ``mapper`` to an object. The
+routes inside the mapper will be used to find an object to publish. 
+
+Usage
+-----
+
+Register an IRouteRoot adapter for an object which returns the routes
+for that object. This will trigger a custom traverser which will try
+to find a matching route when doing traversal.
+
+
+Conceptual differences
+----------------------
+
+controllers and actions
+~~~~~~~~~~~~~~~~~~~~~~~
+Routes is heavily based around the concepts of ``controllers`` objects which
+have ``action`` methods. Zope 3 works somewhat differently: it uses objects
+for which different views are registered. This is reflected in how routes
+are used.
+
+Instead of calling an ``action`` method on a ``controller``, optionally with
+some named parameters z3c.routes instantiates a ``content item``, optionally
+with some named parameters``, and finds a view for it.
+
+global vs local mappers
+~~~~~~~~~~~~~~~~~~~~~~~
+Most web framework using an application which has a single global mapper
+instance which is used for all queries. In z3c.routes we start routing
+at an object instead of the application root, and accordingly each object
+can have its own mapper.
+
+.. _routes: http://routes.groovie.org/
+


Property changes on: Sandbox/wichert/z3c.routes/trunk/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Sandbox/wichert/z3c.routes/trunk/docs/HISTORY.txt
===================================================================
--- Sandbox/wichert/z3c.routes/trunk/docs/HISTORY.txt	                        (rev 0)
+++ Sandbox/wichert/z3c.routes/trunk/docs/HISTORY.txt	2008-05-03 22:47:05 UTC (rev 86311)
@@ -0,0 +1,8 @@
+Changelog
+=========
+
+1.0 - Unreleased
+----------------
+
+* Initial release
+


Property changes on: Sandbox/wichert/z3c.routes/trunk/docs/HISTORY.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Sandbox/wichert/z3c.routes/trunk/docs/LICENSE.txt
===================================================================
--- Sandbox/wichert/z3c.routes/trunk/docs/LICENSE.txt	                        (rev 0)
+++ Sandbox/wichert/z3c.routes/trunk/docs/LICENSE.txt	2008-05-03 22:47:05 UTC (rev 86311)
@@ -0,0 +1,54 @@
+Zope Public License (ZPL) Version 2.1
+-------------------------------------
+
+A copyright notice accompanies this license document that
+identifies the copyright holders.
+
+This license has been certified as open source. It has also
+been designated as GPL compatible by the Free Software
+Foundation (FSF).
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions in source code must retain the
+   accompanying copyright notice, this list of conditions,
+   and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the accompanying
+   copyright notice, this list of conditions, and the
+   following disclaimer in the documentation and/or other
+   materials provided with the distribution.
+
+3. Names of the copyright holders must not be used to
+   endorse or promote products derived from this software
+   without prior written permission from the copyright
+   holders.
+
+4. The right to distribute this software or to use it for
+   any purpose does not give you the right to use
+   Servicemarks (sm) or Trademarks (tm) of the copyright
+   holders. Use of them is covered by separate agreement
+   with the copyright holders.
+
+5. If any files are modified, you must cause the modified
+   files to carry prominent notices stating that you changed
+   the files and the date of any change.
+
+Disclaimer
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS''
+  AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+  NO EVENT SHALL THE COPYRIGHT HOLDERS BE
+  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+  DAMAGE.


Property changes on: Sandbox/wichert/z3c.routes/trunk/docs/LICENSE.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Sandbox/wichert/z3c.routes/trunk/setup.cfg
===================================================================
--- Sandbox/wichert/z3c.routes/trunk/setup.cfg	                        (rev 0)
+++ Sandbox/wichert/z3c.routes/trunk/setup.cfg	2008-05-03 22:47:05 UTC (rev 86311)
@@ -0,0 +1,3 @@
+[egg_info]
+tag_build = dev
+tag_svn_revision = true

Added: Sandbox/wichert/z3c.routes/trunk/setup.py
===================================================================
--- Sandbox/wichert/z3c.routes/trunk/setup.py	                        (rev 0)
+++ Sandbox/wichert/z3c.routes/trunk/setup.py	2008-05-03 22:47:05 UTC (rev 86311)
@@ -0,0 +1,34 @@
+from setuptools import setup, find_packages
+import os
+
+version = '1.0'
+
+setup(name='z3c.routes',
+      version=version,
+      description="Routes for Zope",
+      long_description=open("README.txt").read() + "\n" +
+                       open(os.path.join("docs", "HISTORY.txt")).read(),
+      # Get more strings from http://www.python.org/pypi?%3Aaction=list_classifiers
+      classifiers=[
+        "Programming Language :: Python",
+        "Topic :: Software Development :: Libraries :: Python Modules",
+        ],
+      keywords='',
+      author='Wichert Akkerman',
+      author_email='wichert at wiggy.net',
+      url='',
+      license='ZPL',
+      packages=find_packages(exclude=['ez_setup']),
+      namespace_packages=['z3c'],
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=[
+          'setuptools',
+          'zope.app.publication',
+          'zope.publisher',
+          'zope.dottedname',
+      ],
+      entry_points="""
+      # -*- Entry points: -*-
+      """,
+      )


Property changes on: Sandbox/wichert/z3c.routes/trunk/setup.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Sandbox/wichert/z3c.routes/trunk/z3c/__init__.py
===================================================================
--- Sandbox/wichert/z3c.routes/trunk/z3c/__init__.py	                        (rev 0)
+++ Sandbox/wichert/z3c.routes/trunk/z3c/__init__.py	2008-05-03 22:47:05 UTC (rev 86311)
@@ -0,0 +1,6 @@
+# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)


Property changes on: Sandbox/wichert/z3c.routes/trunk/z3c/__init__.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Sandbox/wichert/z3c.routes/trunk/z3c/routes/.traverser.py.swp
===================================================================
(Binary files differ)


Property changes on: Sandbox/wichert/z3c.routes/trunk/z3c/routes/.traverser.py.swp
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Sandbox/wichert/z3c.routes/trunk/z3c/routes/__init__.py
===================================================================
--- Sandbox/wichert/z3c.routes/trunk/z3c/routes/__init__.py	                        (rev 0)
+++ Sandbox/wichert/z3c.routes/trunk/z3c/routes/__init__.py	2008-05-03 22:47:05 UTC (rev 86311)
@@ -0,0 +1 @@
+


Property changes on: Sandbox/wichert/z3c.routes/trunk/z3c/routes/__init__.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Sandbox/wichert/z3c.routes/trunk/z3c/routes/interfaces.py
===================================================================
--- Sandbox/wichert/z3c.routes/trunk/z3c/routes/interfaces.py	                        (rev 0)
+++ Sandbox/wichert/z3c.routes/trunk/z3c/routes/interfaces.py	2008-05-03 22:47:05 UTC (rev 86311)
@@ -0,0 +1,37 @@
+from zope.interface import Interface
+from zope.interface import Attribute
+
+class IRoutingRoot(Interface):
+    """Mark an object as a possible root for route based traversing.
+    """
+
+    def routes():
+        """Return all routes associated with this object."""
+
+
+class IRoute(Interface):
+    """Route.
+
+    A route maps a URL path to an object and a view for it
+    """
+
+    route = Attribute("Route specifier")
+    factory = Attribute("Object factory.")
+    arguments = Attribute("Extra arguments passed to the object factory")
+
+    def __init__(route, factory, **kwargs):
+        """Create a new route."""
+
+
+    def match(path, request):
+        """Test if this route matches a given request.
+        """
+
+    def execute(request):
+        """Execute this route.
+
+        You may only call this method if match() has already been called
+        and returned succes.
+        """
+
+


Property changes on: Sandbox/wichert/z3c.routes/trunk/z3c/routes/interfaces.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Sandbox/wichert/z3c.routes/trunk/z3c/routes/route.py
===================================================================
--- Sandbox/wichert/z3c.routes/trunk/z3c/routes/route.py	                        (rev 0)
+++ Sandbox/wichert/z3c.routes/trunk/z3c/routes/route.py	2008-05-03 22:47:05 UTC (rev 86311)
@@ -0,0 +1,50 @@
+import re
+from zope.dottedname.resolve import resolve
+from zope.interface import implements
+from z3c.routes.interfaces import IRoute
+
+class Route(object):
+    implements(IRoute)
+
+    variable_matcher = re.compile(r"/:([^/]+)")
+
+    def __init__(self, route, factory, **kwargs):
+        self.route=route
+        self.variables=kwargs
+
+        if isinstance(factory, basestring):
+            self.factory=resolve(factory)
+        else:
+            self.factory=factory
+
+        if not callable(self.factory):
+            raise TypeError("Factory has to be a (dotted name of a) callable")
+
+        self._compile()
+
+
+    def _compile(self):
+        matcher=self.variable_matcher.sub(r"/(?P<\1>[^/]+)", self.route)
+        matcher+=r"\b"
+        self._matcher=re.compile(matcher)
+
+
+    def match(self, path, request):
+        matches=self._matcher.match(path)
+        if matches is None:
+            return False
+
+        variables=self.variables.copy()
+        variables.update(matches.groupdict())
+        request.annotations["z3c.routes.variables"]=variables
+        request.annotations["z3c.routes.path"]=path[:matches.end()]
+        return True
+
+
+    def execute(self, request):
+        parts=filter(None, request.annotations["z3c.routes.path"].split("/"))
+        request.setTraversalStack(request.getTraversalStack()[len(parts)-1:])
+
+        return self.factory(**request.annotations["z3c.routes.variables"])
+
+


Property changes on: Sandbox/wichert/z3c.routes/trunk/z3c/routes/route.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Sandbox/wichert/z3c.routes/trunk/z3c/routes/tests/__init__.py
===================================================================
--- Sandbox/wichert/z3c.routes/trunk/z3c/routes/tests/__init__.py	                        (rev 0)
+++ Sandbox/wichert/z3c.routes/trunk/z3c/routes/tests/__init__.py	2008-05-03 22:47:05 UTC (rev 86311)
@@ -0,0 +1 @@
+


Property changes on: Sandbox/wichert/z3c.routes/trunk/z3c/routes/tests/__init__.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Sandbox/wichert/z3c.routes/trunk/z3c/routes/tests/testRoute.py
===================================================================
--- Sandbox/wichert/z3c.routes/trunk/z3c/routes/tests/testRoute.py	                        (rev 0)
+++ Sandbox/wichert/z3c.routes/trunk/z3c/routes/tests/testRoute.py	2008-05-03 22:47:05 UTC (rev 86311)
@@ -0,0 +1,116 @@
+from unittest import TestCase
+from unittest import TestSuite
+from unittest import makeSuite
+from zope.interface.verify import verifyClass
+from z3c.routes.route import Route
+from z3c.routes.interfaces import IRoute
+
+def DummyFactory(*args, **kwargs):
+    return (args, kwargs)
+
+class DummyRequest:
+    def __init__(self, path=[]):
+        self.path=path
+        self.annotations={}
+
+    def getTraversalStack(self):
+        return self.path
+
+    def setTraversalStack(self, path):
+        self.path[:]=path
+
+
+
+class ConstructorTests(TestCase):
+    def testInterface(self):
+        verifyClass(IRoute, Route)
+
+    def testDottedFactory(self):
+        route=Route("", "z3c.routes.tests.testRoute.test_suite")
+        self.failUnless(route.factory is test_suite)
+
+    def testFactoryMustBeCallable(self):
+        self.assertRaises(TypeError, Route, None, ["not a factory"])
+
+    def testCompileSimpleRoute(self):
+        route=Route("/", DummyFactory)
+        self.assertEqual(route._matcher.pattern, r"/\b")
+
+    def testCompileRouteWithVariable(self):
+        route=Route("/:model/view", DummyFactory)
+        self.assertEqual(route._matcher.pattern, r"/(?P<model>[^/]+)/view\b")
+
+    def testCompileRouteWithMultipleVariables(self):
+        route=Route("/:model/view/:id", DummyFactory)
+        self.assertEqual(route._matcher.pattern, r"/(?P<model>[^/]+)/view/(?P<id>[^/]+)\b")
+
+
+
+class MatchTests(TestCase):
+    def testSimpleString(self):
+        route=Route("/view", DummyFactory)
+        self.assertEqual(route.match("/view", DummyRequest()), True)
+        self.assertEqual(route.match("/notview", DummyRequest()), False)
+
+    def testPrefixElementDoesNotMatch(self):
+        route=Route("/view", DummyFactory)
+        self.assertEqual(route.match("/viewme", DummyRequest()), False)
+
+    def testPrefixPathMatches(self):
+        route=Route("/view", DummyFactory)
+        self.assertEqual(route.match("/view/other", DummyRequest()), True)
+
+    def testMatchPathStored(self):
+        route=Route("/view", DummyFactory)
+        request=DummyRequest()
+        route.match("/view/other", request)
+        self.assertEqual(request.annotations["z3c.routes.path"], "/view")
+
+    def testVariablesExtracted(self):
+        route=Route("/:model/view", DummyFactory)
+        request=DummyRequest()
+        route.match("/trains/view", request)
+        request_variables=request.annotations["z3c.routes.variables"]
+        self.assertEqual(request_variables.get("model", None), "trains")
+
+    def testVariableExtractionDoesNotOverwriteDefaults(self):
+        route=Route("/:model/view", DummyFactory, models="planes")
+        route.match("/trains/view", DummyRequest())
+        self.failUnless(route.variables.get("models", None), "planes")
+
+
+
+class ExecuteTests(TestCase):
+    def testVariablesPassedOn(self):
+        route=Route("", DummyFactory, key="value")
+        request=DummyRequest()
+        request.annotations["z3c.routes.variables"]=dict(key="value")
+        request.annotations["z3c.routes.path"]=""
+        result=route.execute(request)
+        self.assertEqual(result, ((), dict(key="value")))
+
+    def testTraversalStackCleanSingleLevel(self):
+        route=Route("", DummyFactory, key="value")
+        request=DummyRequest(["branch", "leaf"])
+        request.annotations["z3c.routes.variables"]=dict(key="value")
+        request.annotations["z3c.routes.path"]="/toplevel"
+        route.execute(request)
+        self.assertEqual(request.path, ["branch", "leaf"])
+
+    def testTraversalStackCleanedTwoLevels(self):
+        route=Route("", DummyFactory, key="value")
+        request=DummyRequest(["branch", "leaf"])
+        request.annotations["z3c.routes.variables"]=dict(key="value")
+        request.annotations["z3c.routes.path"]="/toplevel/branch"
+        route.execute(request)
+        self.assertEqual(request.path, ["leaf"])
+
+
+
+def test_suite():
+    suite=TestSuite()
+    suite.addTest(makeSuite(ConstructorTests))
+    suite.addTest(makeSuite(MatchTests))
+    suite.addTest(makeSuite(ExecuteTests))
+    return suite
+


Property changes on: Sandbox/wichert/z3c.routes/trunk/z3c/routes/tests/testRoute.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Sandbox/wichert/z3c.routes/trunk/z3c/routes/tests/testTraverse.py
===================================================================
--- Sandbox/wichert/z3c.routes/trunk/z3c/routes/tests/testTraverse.py	                        (rev 0)
+++ Sandbox/wichert/z3c.routes/trunk/z3c/routes/tests/testTraverse.py	2008-05-03 22:47:05 UTC (rev 86311)
@@ -0,0 +1,43 @@
+from unittest import TestCase
+from unittest import TestSuite
+from unittest import makeSuite
+from zope.component import provideAdapter
+from zope.component import adapts
+from zope.interface import implements
+from zope.interface import Interface
+from zope.testing.cleanup import CleanUp
+from zope.interface.verify import verifyClass
+from zope.publisher.interfaces import IPublishTraverse
+from z3c.routes.traverser import RouteTraverser
+from z3c.routes.interfaces import IRoutingRoot
+from zope.publisher.interfaces import NotFound
+
+class DummyRequest:
+    def getTraversalStack(self):
+        return []
+
+class NothingRouter(object):
+    implements(IRoutingRoot)
+    adapts(Interface)
+
+    def __init__(self, context):
+        pass
+    def routes(self):
+        return []
+
+
+class TraversalTests(TestCase, CleanUp):
+    def testInterface(self):
+        verifyClass(IPublishTraverse, RouteTraverser)
+
+    def testNotFoundRaisedIfNothingMatches(self):
+        provideAdapter(NothingRouter)
+        traverser=RouteTraverser(None, None)
+        self.assertRaises(NotFound,
+                          traverser.publishTraverse, DummyRequest(), "nothere")
+
+
+def test_suite():
+    suite=TestSuite()
+    suite.addTest(makeSuite(TraversalTests))
+    return suite


Property changes on: Sandbox/wichert/z3c.routes/trunk/z3c/routes/tests/testTraverse.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Sandbox/wichert/z3c.routes/trunk/z3c/routes/traverser.py
===================================================================
--- Sandbox/wichert/z3c.routes/trunk/z3c/routes/traverser.py	                        (rev 0)
+++ Sandbox/wichert/z3c.routes/trunk/z3c/routes/traverser.py	2008-05-03 22:47:05 UTC (rev 86311)
@@ -0,0 +1,27 @@
+from zope.interface import implements
+from zope.component import adapts
+from zope.publisher.interfaces import NotFound
+from zope.publisher.interfaces import IPublishTraverse
+from zope.publisher.interfaces.http import IHTTPRequest
+from z3c.routes.interfaces import IRoutingRoot
+
+
+class RouteTraverser(object):
+    implements(IPublishTraverse)
+    adapts(IRoutingRoot, IHTTPRequest)
+
+    def __init__(self, context, request):
+        self.context=context
+        self.request=request
+
+
+    def publishTraverse(self, request, name):
+        path=name+"/".join(request.getTraversalStack())
+        for route in IRoutingRoot(self.context).routes():
+            if route.match(path, request):
+                return route.execute(request)
+        else:
+            raise NotFound(self.context, name)
+
+
+


Property changes on: Sandbox/wichert/z3c.routes/trunk/z3c/routes/traverser.py
___________________________________________________________________
Name: svn:eol-style
   + native



More information about the Checkins mailing list