[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