summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-05-16 18:48:02 +0000
committerChris McDonough <chrism@agendaless.com>2009-05-16 18:48:02 +0000
commit29e01279ff0b13623a6b1b769351632f12bafb35 (patch)
treeb53e44b99df1f4fdf5f3437e7228b53652e53fc0
parentdfad1fbd9fa46c67aee8de3c3d3b36c0af1ec7bf (diff)
downloadpyramid-29e01279ff0b13623a6b1b769351632f12bafb35.tar.gz
pyramid-29e01279ff0b13623a6b1b769351632f12bafb35.tar.bz2
pyramid-29e01279ff0b13623a6b1b769351632f12bafb35.zip
- The ``RoutesMapper`` class in ``repoze.bfg.urldispatch`` has been
removed, as well as its documentation. It had been deprecated since 0.6.3. Code in ``repoze.bfg.urldispatch.RoutesModelTraverser`` which catered to it has also been removed. - The semantics of the ``route`` ZCML directive have been simplified. Previously, it was assumed that to use a route, you wanted to map a route to an externally registered view. The new ``route`` directive instead has a ``view`` attribute which is required, specifying the dotted path to a view callable. When a route directive is processed, a view is *registered* using the name attribute of the route directive as its name and the callable as its value. The ``view_name`` and ``provides`` attributes of the ``route`` directive are therefore no longer used. Effectively, if you were previously using the ``route`` directive, it means you must change a pair of ZCML directives that look like this:: <route name="home" path="" view_name="login" factory=".models.root.Root" /> <view for=".models.root.Root" name="login" view=".views.login_view" /> To a ZCML directive that looks like this:: <route name="home" path="" view=".views.login_view" factory=".models.root.Root" /> In other words, to make old code work, remove the ``view`` directives that were only there to serve the purpose of backing ``route`` directives, and move their ``view=`` attribute into the ``route`` directive itself. This change also necessitated that the ``name`` attribute of the ``route`` directive is now required. If you were previously using ``route`` directives without a ``name`` attribute, you'll need to add one (the name is arbitrary, but must be unique among all ``route`` and ``view`` statements). The ``provides`` attribute of the ``route`` directive has also been removed. This directive specified a sequence of interface types that the generated context would be decorated with. Since route views are always generated now for a single interface (``repoze.bfg.IRoutesContext``) as opposed to being looked up arbitrarily, there is no need to decorate any context to ensure a view is found. - The Routes ``Route`` object used to resolve the match is now put into the environment as ``bfg.route`` when URL dispatch is used.
-rw-r--r--CHANGES.txt64
-rw-r--r--docs/narr/urldispatch.rst247
-rw-r--r--repoze/bfg/interfaces.py2
-rw-r--r--repoze/bfg/paster_templates/routesalchemy/+package+/configure.zcml14
-rw-r--r--repoze/bfg/tests/routesapp/configure.zcml13
-rw-r--r--repoze/bfg/tests/test_urldispatch.py212
-rw-r--r--repoze/bfg/tests/test_zcml.py128
-rw-r--r--repoze/bfg/urldispatch.py114
-rw-r--r--repoze/bfg/zcml.py91
9 files changed, 381 insertions, 504 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index a72220217..0a7610a7c 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,6 +1,67 @@
0.8dev
======
+Backwards Incompatibilities
+---------------------------
+
+- The ``RoutesMapper`` class in ``repoze.bfg.urldispatch`` has been
+ removed, as well as its documentation. It had been deprecated since
+ 0.6.3. Code in ``repoze.bfg.urldispatch.RoutesModelTraverser``
+ which catered to it has also been removed.
+
+- The semantics of the ``route`` ZCML directive have been simplified.
+ Previously, it was assumed that to use a route, you wanted to map a
+ route to an externally registered view. The new ``route`` directive
+ instead has a ``view`` attribute which is required, specifying the
+ dotted path to a view callable. When a route directive is
+ processed, a view is *registered* using the name attribute of the
+ route directive as its name and the callable as its value. The
+ ``view_name`` and ``provides`` attributes of the ``route`` directive
+ are therefore no longer used. Effectively, if you were previously
+ using the ``route`` directive, it means you must change a pair of
+ ZCML directives that look like this::
+
+ <route
+ name="home"
+ path=""
+ view_name="login"
+ factory=".models.root.Root"
+ />
+
+ <view
+ for=".models.root.Root"
+ name="login"
+ view=".views.login_view"
+ />
+
+ To a ZCML directive that looks like this::
+
+ <route
+ name="home"
+ path=""
+ view=".views.login_view"
+ factory=".models.root.Root"
+ />
+
+ In other words, to make old code work, remove the ``view``
+ directives that were only there to serve the purpose of backing
+ ``route`` directives, and move their ``view=`` attribute into the
+ ``route`` directive itself.
+
+ This change also necessitated that the ``name`` attribute of the
+ ``route`` directive is now required. If you were previously using
+ ``route`` directives without a ``name`` attribute, you'll need to
+ add one (the name is arbitrary, but must be unique among all
+ ``route`` and ``view`` statements).
+
+ The ``provides`` attribute of the ``route`` directive has also been
+ removed. This directive specified a sequence of interface types
+ that the generated context would be decorated with. Since route
+ views are always generated now for a single interface
+ (``repoze.bfg.IRoutesContext``) as opposed to being looked up
+ arbitrarily, there is no need to decorate any context to ensure a
+ view is found.
+
Documentation
-------------
@@ -26,6 +87,9 @@ Features
Routes areused). This template can be used via ``paster create -t
bfg_alchemy``.
+- The Routes ``Route`` object used to resolve the match is now put
+ into the environment as ``bfg.route`` when URL dispatch is used.
+
0.8a6 (2009-05-11)
==================
diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst
index 5bb81350c..028c184db 100644
--- a/docs/narr/urldispatch.rst
+++ b/docs/narr/urldispatch.rst
@@ -9,19 +9,15 @@ also map URLs to code via :term:`URL dispatch` using the
:term:`Routes` framework. The :term:`Routes` framework is a Python
reimplementation of the `Rails routes system
<http://manuals.rubyonrails.com/read/chapter/65>`_. It is a mechanism
-which allows you to declaratively map URLs to code. Both traversal
-and URL dispatch have the same goal: to find the context and the view
-name.
+which allows you to declaratively map URLs to code.
.. note:: In common :term:`Routes` lingo, the code that it maps URLs
to is defined by a *controller* and an *action*. However,
neither concept (controller nor action) exists within
:mod:`repoze.bfg`. Instead, when you map a URL pattern to
- code in bfg, you will map the URL patterm to a
- :term:`context` and a :term:`view name`. Once the context
- and view name are found, the same :term:`view` lookup which
- is detailed in :ref:`traversal_chapter` will be done using
- the context and view name found via a route.
+ code in bfg, you will map the URL patterm to a :term:`view`.
+ Once the context and view name are found, the view will be
+ called with a :term:`context` and a :term:`request`.
It often makes a lot of sense to use :term:`URL dispatch` instead of
:term:`traversal` in an application that has no natural hierarchy.
@@ -51,19 +47,20 @@ allows you to inject ``route`` ZCML directives into your application's
:mod:`repoze.bfg`.
When any ``route`` ZCML directive is present in an application's
-``configure.zcml``, "under the hood" :mod:`repoze.bfg` wraps the "root
-factory" in a special ``RoutesRootFactory`` instance. The wrapper
-instance then acts as the root factory. When it acts as a root
-factory, it is willing to check the requested URL against a *routes
-map* to find the :term:`context` and the :term:`view name` before
-traversal has a chance to find it first. If it finds a context and a
-view name via a route, :mod:`repoze.bfg` will attempt to look up and
-call a :mod:`repoze.bfg` :term:`view` that matches the context and the
-view name. If no route matches, :mod:`repoze.bfg` will fail over to
-calling the root factory callable passed to the application in it's
-``make_app`` function (usually a traversal function). By configuring
-your ZCML ``route`` statements appropriately, you can mix and match
-URL dispatch and traversal in this way.
+``configure.zcml``, "under the hood" :mod:`repoze.bfg` wraps the
+:term:`root factory` in a special ``RoutesRootFactory`` instance. The
+wrapper instance then acts as the effective root factory. When it
+acts as a root factory, it is willing to check the requested URL
+against a *routes map* to find a :term:`context` and a :term:`view`
+before traversal has a chance to find it first. If a route matches, a
+:term:`context` is generated and :mod:`repoze.bfg` will call the
+:term:`view` specified with the context and the request.
+
+If no route matches, :mod:`repoze.bfg` will fail over to calling the
+root factory callable passed to the application in it's ``make_app``
+function (usually a traversal function). By configuring your ZCML
+``route`` statements appropriately, you can mix and match URL dispatch
+and traversal in this way.
A root factory is not required for purely URL-dispatch-based apps: if
the root factory callable is ``None``, :mod:`repoze.bfg` will return a
@@ -88,25 +85,27 @@ name
The `route name
<http://routes.groovie.org/manual.html#route-name>`_,
- e.g. ``myroute``.
+ e.g. ``myroute``. This attribute is required.
-view_name
+view
- The :mod:`repoze.bfg` :term:`view name` that should be looked up
- when this route matches a URL.
+ The Python dotted-path name to a function that will be used as a
+ view callable when this route matches.
+ e.g. ``mypackage.views.my_view``. This attribute is required.
-factory
+permission
- The Python dotted-path name to a function that will generate a
- :mod:`repoze.bfg` context object when this route matches. By
- default, a ``repoze.bfg.urldispatch.DefaultRoutesContext`` object
- will be constructed if a factory is not provided.
+ The permission name required to invoke the view.
+ e.g. ``edit``. (see :ref:`using_security_with_urldispatch` for more
+ information about permissions).
-provides
+factory
- One or more Python-dotted path names to :term:`interface` objects
- that the context should be decorated with when it's constructed
- (allowing it to be found by a particular view lookup).
+ The Python dotted-path name to a function that will generate a
+ :mod:`repoze.bfg` context object when this route matches.
+ e.g. ``mypackage.models.MyFactoryClass``. By default, a
+ ``repoze.bfg.urldispatch.DefaultRoutesContext`` object will be
+ constructed if a factory is not provided.
encoding
@@ -179,39 +178,85 @@ that allows you to specify Routes `requirement
For example:
.. code-block:: xml
+ :linenos:
- <route path="archives/:year/:month">
+ <route
+ name="archive"
+ path="archives/:year/:month"
+ view=".views.archive_view">
<requirement
attr="year"
- expr="d{2,4}"/>
+ expr="d{2,4}"
+ />
<requirement
attr="month"
- expr="d{1,2}"/>
+ expr="d{1,2}"
+ />
</route>
Example 1
---------
-Below is an example of some route statements you might add to your
-``configure.zcml``:
+The simplest route delcaration:
.. code-block:: xml
:linenos:
<route
+ name="idea"
+ path="hello.html"
+ view="mypackage.views.hello_view"
+ />
+
+When the URL matches ``/hello.html``, the view callable at the Python
+dotted path name ``mypackage.views.hello_view`` will be called with a
+default context object and the request. See :ref:`views_chapter` for
+more information about views.
+
+The ``mypackage.views`` module referred to above might look like so:
+
+.. code-block:: python
+ :linenos:
+
+ from webob import Response
+
+ def hello_view(context, request):
+ return Response('Hello!')
+
+In this case the context object passed to the view will be an instance
+of the ``repoze.bfg.urldispatch.DefaultRoutesContext``. This is the
+type of obejct created for a context when there is no "factory"
+specified in the ``route`` declaration.
+
+Example 2
+---------
+
+Below is an example of some more complicated route statements you
+might add to your ``configure.zcml``:
+
+.. code-block:: xml
+ :linenos:
+
+ <route
+ name="idea"
path="ideas/:idea"
- view_name="ideas"/>
+ view="mypackage.views.idea_view"
+ />
<route
+ name="user"
path="users/:user"
- view_name="users"/>
+ view="mypackage.views.user_view"
+ />
- <route
+ <route
+ name="tag"
path="tags/:tag"
- view_name="tags"/>
+ view="mypackage.views.tag_view"
+ />
The above configuration will allow :mod:`repoze.bfg` to service URLs
in these forms:
@@ -224,10 +269,12 @@ in these forms:
/tags/<tagname>
When a URL matches the pattern ``/ideas/<ideaname>``, the view
-registered with the name ``ideas`` for the interface
-``repoze.bfg.interfaces.IRoutesContext`` will be called. An error
-will be raised if no view can be found with that interface type and
-view name combination.
+registered with the name ``idea`` will be called. This will be the
+view available at the dotted Python pathname
+``mypackage.views.idea_view``.
+
+Example 3
+---------
The context object passed to a view found as the result of URL
dispatch will by default be an instance of the
@@ -243,46 +290,31 @@ An example of using a route with a factory:
:linenos:
<route
+ name="idea"
path="ideas/:idea"
+ view=".views.idea_view"
factory=".models.Idea"
- view_name="ideas"/>
+ />
The above route will manufacture an ``Idea`` model as a context,
-assuming that ``.models.Idea`` resolves to a class that accepts
-arbitrary key/value pair arguments.
+assuming that ``mypackage.models.Idea`` resolves to a class that
+accepts arbitrary key/value pair arguments.
.. note:: Values prefixed with a period (``.``) for the ``factory``
and ``provides`` attributes of a ``route`` (such as
- ``.models.Idea`` above) mean "relative to the Python package
- directory in which this :term:`ZCML` file is stored". So if the
- above ``route`` declaration was made inside a ``configure.zcml``
- file that lived in the ``hello`` package, you could replace the
- relative ``.models.Idea`` with the absolute ``hello.models.Idea``
- Either the relative or absolute form is functionally equivalent.
- It's often useful to use the relative form, in case your package's
- name changes. It's also shorter to type.
+ ``.models.Idea`` and ``.views.idea_view``) above) mean "relative to
+ the Python package directory in which this :term:`ZCML` file is
+ stored". So if the above ``route`` declaration was made inside a
+ ``configure.zcml`` file that lived in the ``hello`` package, you
+ could replace the relative ``.models.Idea`` with the absolute
+ ``hello.models.Idea`` Either the relative or absolute form is
+ functionally equivalent. It's often useful to use the relative
+ form, in case your package's name changes. It's also shorter to
+ type.
All context objects manufactured via URL dispatch will be decorated by
default with the ``repoze.bfg.interfaces.IRoutesContext``
-:term:`interface`. To decorate a context found via a route with other
-interfaces, you can use a ``provides`` attribute on the ZCML
-statement. It should be a space-separated list of dotted Python names
-that point at interface definitions.
-
-An example of using a route with a set of ``provides`` interfaces:
-
-.. code-block:: xml
- :linenos:
-
- <route
- path="ideas/:idea"
- provides=".interfaces.IIdea .interfaces.IContent"
- view_name="ideas"/>
-
-The above route will manufacture an instance of
-``DefaultRoutesContext`` as a context; it will be decorate with the
-``.interfaces.IIdea`` and ``.interfaces.IContent`` interfaces, as long
-as those dotted names resolve to interfaces.
+:term:`interface`.
If no route matches in the above configuration, :mod:`repoze.bfg` will
call the "fallback" ``get_root`` callable provided to it during
@@ -294,7 +326,7 @@ error will be raised when no route matches.
context. You can also map classes to views; interfaces are
not used then.
-Example 2
+Example 4
---------
An example of configuring a ``view`` declaration in ``configure.zcml``
@@ -304,24 +336,14 @@ function is as follows:
.. code-block:: xml
:linenos:
- <view
- for=".interfaces.ISomeContext"
- view=".views.articles_view"
- name="articles"
- />
-
<route
+ name="article"
path="archives/:article"
- view_name="articles"
+ view=".views.article_view"
factory=".models.Article"
- provides=".interfaces.ISomeContext"
/>
-All context objects found via Routes URL dispatch will provide the
-``IRoutesContext`` interface (attached dynamically). The above
-``route`` statement will also cause contexts generated by the route to
-have the ``.interfaces.ISomeContext`` interface as well. The
-``.models`` modulemight look like so:
+The ``.models`` module referred to above might look like so:
.. code-block:: python
:linenos:
@@ -330,6 +352,16 @@ have the ``.interfaces.ISomeContext`` interface as well. The
def __init__(self, **kw):
self.__dict__.update(kw)
+The ``.views`` module referred to above might look like so:
+
+.. code-block:: python
+ :linenos:
+
+ from webob import Response
+
+ def article_view(context, request):
+ return Response('Article with name' % context.article)
+
The effect of this configuration: when this :mod:`repoze.bfg`
application runs, if any URL matches the pattern
``archives/:article``, the ``.views.articles_view`` view will be
@@ -343,29 +375,6 @@ In this case in particular, when a user visits
Article class and it will have an ``article`` attribute with the value
of ``something``.
-Example 3
----------
-
-You can also make the ``view_name`` into a routes path argument
-instead of specifying it as an argument:
-
-.. code-block:: xml
- :linenos:
-
- <view
- for="repoze.bfg.interfaces.IRoutesContext"
- view=".views.articles_view"
- name="articles"
- />
-
- <route
- path="archives/:view_name"
- />
-
-When you do this, the :term:`view name` will be computed dynamically if
-the route matches. In the above example, if the ``view_name`` turns
-out to be ``articles``, the articles view will eventually be called.
-
Catching the Root URL
---------------------
@@ -379,7 +388,7 @@ declaration:
<route
path=""
name="root"
- view_name="root_view"
+ view=".views.root_view"
/>
Cleaning Up After a Request
@@ -425,6 +434,8 @@ Then in the ``configure.zcml`` of your package, inject the following:
This will cause the DBSession to be removed whenever the WSGI
environment is destroyed (usually at the end of every request).
+.. _using_security_with_urldispatch:
+
Using :mod:`repoze.bfg` Security With URL Dispatch
--------------------------------------------------
@@ -471,9 +482,3 @@ the ``Article`` class' constructor, too.
:term:`Routes` manual for a general overview of what the
``condition`` argument to ``.connect`` does.
-Further Documentation and Examples
-----------------------------------
-
-The API documentation in :ref:`urldispatch_module` documents an older
-(now-deprecated) version of Routes support in :mod:`repoze.bfg`.
-
diff --git a/repoze/bfg/interfaces.py b/repoze/bfg/interfaces.py
index f9a0a17e5..dbbfd78a5 100644
--- a/repoze/bfg/interfaces.py
+++ b/repoze/bfg/interfaces.py
@@ -29,7 +29,7 @@ class IDELETERequest(IRequest):
class IHEADRequest(IRequest):
""" Request type interface attached to HEAD requests"""
-
+
class IResponseFactory(Interface):
""" A utility which generates a response factory """
def __call__():
diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/configure.zcml b/repoze/bfg/paster_templates/routesalchemy/+package+/configure.zcml
index aca4566bc..c297209f4 100644
--- a/repoze/bfg/paster_templates/routesalchemy/+package+/configure.zcml
+++ b/repoze/bfg/paster_templates/routesalchemy/+package+/configure.zcml
@@ -8,17 +8,13 @@
<route path=""
name="home"
+ view=".views.my_view"
factory=".models.get_root_model"
/>
- <view
- for=".models.Model"
- view=".views.my_view"
- />
-
- <view
- view=".views.static_view"
- name="static"
- />
+ <route path="static/*subpath"
+ name="static"
+ view=".views.static_view"
+ />
</configure>
diff --git a/repoze/bfg/tests/routesapp/configure.zcml b/repoze/bfg/tests/routesapp/configure.zcml
index 17653615a..01062b6d4 100644
--- a/repoze/bfg/tests/routesapp/configure.zcml
+++ b/repoze/bfg/tests/routesapp/configure.zcml
@@ -4,14 +4,9 @@
<route
path=":id"
- provides=".models.IFixture"
- view_name="default"/>
-
- <view
- name="default"
- view=".views.fixture_view"
- for=".models.IFixture"
- permission="repoze.view"
- />
+ name="default"
+ view=".views.fixture_view"
+ permission="repoze.view"
+ />
</configure>
diff --git a/repoze/bfg/tests/test_urldispatch.py b/repoze/bfg/tests/test_urldispatch.py
index e08e98ca7..e1640bfdf 100644
--- a/repoze/bfg/tests/test_urldispatch.py
+++ b/repoze/bfg/tests/test_urldispatch.py
@@ -1,80 +1,5 @@
import unittest
-class RoutesMapperTests(unittest.TestCase):
- def setUp(self):
- from zope.deprecation import __show__
- __show__.off()
-
- def tearDown(self):
- from zope.deprecation import __show__
- __show__.on()
-
- 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)
- self.assertEqual(mapper.mapper.environ, environ)
-
- 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)
- from repoze.bfg.interfaces import IRoutesContext
- self.failUnless(IRoutesContext.providedBy(result))
- self.assertEqual(result.controller, 'foo')
- self.assertEqual(result.action, 'action1')
- self.assertEqual(result.article, 'article1')
-
- def test_routes_mapper_custom_context_factory(self):
- marker = ()
- get_root = make_get_root(marker)
- mapper = self._makeOne(get_root)
- class Dummy(object):
- def __init__(self, **kw):
- self.__dict__.update(kw)
- mapper.connect('archives/:action/:article', controller='foo',
- context_factory=Dummy)
- 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')
- from repoze.bfg.interfaces import IRoutesContext
- self.failUnless(IRoutesContext.providedBy(result))
- self.failUnless(isinstance(result, Dummy))
- self.failIf(hasattr(result, 'context_factory'))
-
- 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 RoutesRootFactoryTests(unittest.TestCase):
def _getEnviron(self, **kw):
environ = {'SERVER_NAME':'localhost',
@@ -103,18 +28,19 @@ class RoutesRootFactoryTests(unittest.TestCase):
marker = ()
get_root = make_get_root(marker)
mapper = self._makeOne(get_root)
- mapper.connect('archives/:action/:article', view_name='foo')
+ mapper.connect('foo', 'archives/:action/:article', foo='foo')
environ = self._getEnviron(PATH_INFO='/archives/action1/article1')
result = mapper(environ)
from repoze.bfg.interfaces import IRoutesContext
self.failUnless(IRoutesContext.providedBy(result))
- self.assertEqual(result.view_name, 'foo')
+ self.assertEqual(result.foo, 'foo')
self.assertEqual(result.action, 'action1')
self.assertEqual(result.article, 'article1')
routing_args = environ['wsgiorg.routing_args'][1]
- self.assertEqual(routing_args['view_name'], 'foo')
+ self.assertEqual(routing_args['foo'], 'foo')
self.assertEqual(routing_args['action'], 'action1')
self.assertEqual(routing_args['article'], 'article1')
+ self.assertEqual(environ['bfg.route'].name, 'foo')
def test_unnamed_root_route_matches(self):
mapper = self._makeOne(None)
@@ -123,18 +49,28 @@ class RoutesRootFactoryTests(unittest.TestCase):
result = mapper(environ)
from repoze.bfg.interfaces import IRoutesContext
self.failUnless(IRoutesContext.providedBy(result))
+ self.assertEqual(environ['bfg.route'].name, None)
+
+ def test_named_root_route_matches(self):
+ mapper = self._makeOne(None)
+ mapper.connect('root', '')
+ environ = self._getEnviron(PATH_INFO='/')
+ result = mapper(environ)
+ from repoze.bfg.interfaces import IRoutesContext
+ self.failUnless(IRoutesContext.providedBy(result))
+ self.assertEqual(environ['bfg.route'].name, 'root')
def test_unicode_in_route_default(self):
marker = ()
get_root = make_get_root(marker)
mapper = self._makeOne(get_root)
- class DummyRoute:
+ class DummyRoute2:
routepath = ':id'
_factory = None
_provides = ()
la = unicode('\xc3\xb1a', 'utf-8')
- mapper.routematch = lambda *arg: ({la:'id'}, DummyRoute)
- mapper.connect(':la')
+ mapper.routematch = lambda *arg: ({la:'id'}, DummyRoute2)
+ mapper.connect('whatever', ':la')
environ = self._getEnviron(PATH_INFO='/foo')
result = mapper(environ)
from repoze.bfg.interfaces import IRoutesContext
@@ -146,7 +82,7 @@ class RoutesRootFactoryTests(unittest.TestCase):
def test_no_fallback_get_root(self):
marker = ()
mapper = self._makeOne(None)
- mapper.connect('wont/:be/:found', view_name='foo')
+ mapper.connect('wont', 'wont/:be/:found')
environ = self._getEnviron(PATH_INFO='/archives/action1/article1')
result = mapper(environ)
from repoze.bfg.urldispatch import RoutesContextNotFound
@@ -163,11 +99,10 @@ class RoutesRootFactoryTests(unittest.TestCase):
implements(IDummy)
def __init__(self, **kw):
self.__dict__.update(kw)
- mapper.connect('archives/:action/:article', view_name='foo',
+ mapper.connect('article', 'archives/:action/:article',
_factory=Dummy)
environ = self._getEnviron(PATH_INFO='/archives/action1/article1')
result = mapper(environ)
- self.assertEqual(result.view_name, 'foo')
self.assertEqual(result.action, 'action1')
self.assertEqual(result.article, 'article1')
from repoze.bfg.interfaces import IRoutesContext
@@ -176,40 +111,23 @@ class RoutesRootFactoryTests(unittest.TestCase):
self.failUnless(IDummy.providedBy(result))
self.failIf(hasattr(result, '_factory'))
- def test_custom_provides(self):
- marker = ()
- get_root = make_get_root(marker)
- mapper = self._makeOne(get_root)
- from zope.interface import Interface
- class IDummy(Interface):
- pass
- mapper.connect('archives/:action/:article', view_name='foo',
- _provides = [IDummy])
- environ = self._getEnviron(PATH_INFO='/archives/action1/article1')
- result = mapper(environ)
- self.assertEqual(result.view_name, 'foo')
- self.assertEqual(result.action, 'action1')
- self.assertEqual(result.article, 'article1')
- from repoze.bfg.interfaces import IRoutesContext
- self.failUnless(IRoutesContext.providedBy(result))
- self.failUnless(IDummy.providedBy(result))
- self.failIf(hasattr(result, '_provides'))
-
def test_has_routes(self):
mapper = self._makeOne(None)
self.assertEqual(mapper.has_routes(), False)
- mapper.connect('archives/:action/:article', view_name='foo')
+ mapper.connect('whatever', 'archives/:action/:article')
self.assertEqual(mapper.has_routes(), True)
def test_url_for(self):
marker = ()
get_root = make_get_root(marker)
mapper = self._makeOne(get_root)
- mapper.connect('archives/:action/:article', view_name='foo')
+ mapper.connect('whatever', 'archives/:action/:article')
environ = self._getEnviron(PATH_INFO='/archives/action1/article1')
+ route = DummyRoute('yo')
+ environ['bfg.route'] = route
result = mapper(environ)
from routes import url_for
- result = url_for(view_name='foo', action='action2', article='article2')
+ result = url_for(action='action2', article='article2')
self.assertEqual(result, '/archives/action2/article2')
class TestRoutesContextNotFound(unittest.TestCase):
@@ -248,75 +166,29 @@ class RoutesModelTraverserTests(unittest.TestCase):
from repoze.bfg.interfaces import ITraverser
verifyObject(ITraverser, self._makeOne(None))
- def test_call_with_only_controller_bwcompat(self):
- model = DummyContext()
- model.controller = 'controller'
- traverser = self._makeOne(model)
- result = traverser({})
- self.assertEqual(result[0], model)
- self.assertEqual(result[1], 'controller')
- self.assertEqual(result[2], [])
- self.assertEqual(result[3], None)
- self.assertEqual(result[4], model)
- self.assertEqual(result[5], None)
-
- def test_call_with_only_view_name_bwcompat(self):
- model = DummyContext()
- model.view_name = 'view_name'
- traverser = self._makeOne(model)
- result = traverser({})
- self.assertEqual(result[0], model)
- self.assertEqual(result[1], 'view_name')
- self.assertEqual(result[2], [])
- self.assertEqual(result[3], None)
- self.assertEqual(result[4], model)
- self.assertEqual(result[5], None)
-
- def test_call_with_subpath_bwcompat(self):
- model = DummyContext()
- model.view_name = 'view_name'
- model.subpath = '/a/b/c'
- traverser = self._makeOne(model)
- result = traverser({})
- self.assertEqual(result[0], model)
- self.assertEqual(result[1], 'view_name')
- self.assertEqual(result[2], ['a', 'b', 'c'])
- self.assertEqual(result[3], None)
- self.assertEqual(result[4], model)
- self.assertEqual(result[5], None)
-
- def test_call_with_no_view_name_or_controller_bwcompat(self):
+ def test_it_nothingfancy(self):
model = DummyContext()
traverser = self._makeOne(model)
- result = traverser({})
- self.assertEqual(result[0], model)
- self.assertEqual(result[1], '')
- self.assertEqual(result[2], [])
- self.assertEqual(result[3], None)
- self.assertEqual(result[4], model)
- self.assertEqual(result[5], None)
-
- def test_call_with_only_view_name(self):
- model = DummyContext()
- traverser = self._makeOne(model)
- routing_args = ((), {'view_name':'view_name'})
- environ = {'wsgiorg.routing_args': routing_args}
+ routing_args = ((), {})
+ route = DummyRoute('yo')
+ environ = {'wsgiorg.routing_args': routing_args, 'bfg.route': route}
result = traverser(environ)
self.assertEqual(result[0], model)
- self.assertEqual(result[1], 'view_name')
+ self.assertEqual(result[1], 'yo')
self.assertEqual(result[2], [])
self.assertEqual(result[3], None)
self.assertEqual(result[4], model)
self.assertEqual(result[5], None)
- def test_call_with_view_name_and_subpath(self):
+ def test_call_with_subpath(self):
model = DummyContext()
traverser = self._makeOne(model)
- routing_args = ((), {'view_name':'view_name', 'subpath':'/a/b/c'})
- environ = {'wsgiorg.routing_args': routing_args}
+ routing_args = ((), {'subpath':'/a/b/c'})
+ route = DummyRoute('yo')
+ environ = {'wsgiorg.routing_args':routing_args, 'bfg.route': route}
result = traverser(environ)
self.assertEqual(result[0], model)
- self.assertEqual(result[1], 'view_name')
+ self.assertEqual(result[1], 'yo')
self.assertEqual(result[2], ['a', 'b','c'])
self.assertEqual(result[3], None)
self.assertEqual(result[4], model)
@@ -325,12 +197,13 @@ class RoutesModelTraverserTests(unittest.TestCase):
def test_with_path_info(self):
model = DummyContext()
traverser = self._makeOne(model)
- routing_args = ((), {'view_name':'view_name', 'path_info':'foo/bar'})
- environ = {'wsgiorg.routing_args': routing_args,
+ routing_args = ((), {'path_info':'foo/bar'})
+ route = DummyRoute('yo')
+ environ = {'wsgiorg.routing_args': routing_args, 'bfg.route': route,
'PATH_INFO':'/a/b/foo/bar', 'SCRIPT_NAME':''}
result = traverser(environ)
self.assertEqual(result[0], model)
- self.assertEqual(result[1], 'view_name')
+ self.assertEqual(result[1], 'yo')
self.assertEqual(result[2], [])
self.assertEqual(result[3], None)
self.assertEqual(result[4], model)
@@ -341,8 +214,9 @@ class RoutesModelTraverserTests(unittest.TestCase):
def test_with_path_info_PATH_INFO_w_extra_slash(self):
model = DummyContext()
traverser = self._makeOne(model)
- routing_args = ((), {'view_name':'view_name', 'path_info':'foo/bar'})
- environ = {'wsgiorg.routing_args': routing_args,
+ routing_args = ((), {'path_info':'foo/bar'})
+ route = DummyRoute('yo')
+ environ = {'wsgiorg.routing_args': routing_args, 'bfg.route':route,
'PATH_INFO':'/a/b//foo/bar', 'SCRIPT_NAME':''}
result = traverser(environ)
self.assertEqual(environ['PATH_INFO'], '/foo/bar')
@@ -400,3 +274,7 @@ class DummyContext(object):
class DummyRequest(object):
""" """
+class DummyRoute(object):
+ def __init__(self, name):
+ self.name = name
+
diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py
index 0d7f1b631..1853483ba 100644
--- a/repoze/bfg/tests/test_zcml.py
+++ b/repoze/bfg/tests/test_zcml.py
@@ -269,15 +269,15 @@ class TestConnectRouteFunction(unittest.TestCase):
directive = DummyRouteDirective()
self._callFUT(directive)
self.assertEqual(len(mapper.connections), 1)
- self.assertEqual(mapper.connections[0][0], ('a/b/c',))
+ self.assertEqual(mapper.connections[0][0], ('name', 'path'))
self.assertEqual(mapper.connections[0][1], {'requirements': {}})
def test_name_and_path(self):
mapper = self._registerRoutesMapper()
- directive = DummyRouteDirective(name='abc')
+ directive = DummyRouteDirective(name='abc', path='thepath')
self._callFUT(directive)
self.assertEqual(len(mapper.connections), 1)
- self.assertEqual(mapper.connections[0][0], ('abc', 'a/b/c',))
+ self.assertEqual(mapper.connections[0][0], ('abc', 'thepath',))
self.assertEqual(mapper.connections[0][1], {'requirements': {}})
def test_all_directives(self):
@@ -290,28 +290,27 @@ class TestConnectRouteFunction(unittest.TestCase):
parent_member_name='p', parent_collection_name='c',
condition_method='GET', condition_subdomain=True,
condition_function=foo, subdomains=['a'],
- factory=foo, provides=[IDummy], view_name='def')
+ name='thename', path='thepath',
+ factory=foo, view='view', permission='permission')
self._callFUT(directive)
self.assertEqual(len(mapper.connections), 1)
- self.assertEqual(mapper.connections[0][0], ('a/b/c',))
+ self.assertEqual(mapper.connections[0][0], ('thename', 'thepath'))
pr = {'member_name':'p', 'collection_name':'c'}
c = {'method':'GET', 'sub_domain':['a'], 'function':foo}
- self.assertEqual(mapper.connections[0][1],
- {'requirements': {},
- '_minimize':True,
- '_explicit':True,
- '_encoding':'utf-8',
- '_static':True,
- '_filter':foo,
- '_absolute':True,
- '_member_name':'m',
- '_collection_name':'c',
- '_parent_resource':pr,
- 'conditions':c,
- '_factory':foo,
- '_provides':[IDummy],
- 'view_name':'def',
- })
+ D = mapper.connections[0][1]
+
+ self.assertEqual(D['requirements'], {})
+ self.assertEqual(D['_minimize'],True)
+ self.assertEqual(D['_explicit'],True)
+ self.assertEqual(D['_encoding'],'utf-8')
+ self.assertEqual(D['_static'],True)
+ self.assertEqual(D['_filter'],foo)
+ self.assertEqual(D['_absolute'],True)
+ self.assertEqual(D['_member_name'], 'm')
+ self.assertEqual(D['_collection_name'], 'c')
+ self.assertEqual(D['_parent_resource'], pr)
+ self.assertEqual(D['conditions'], c)
+ self.assertEqual(D['_factory'], foo)
def test_condition_subdomain_true(self):
mapper = self._registerRoutesMapper()
@@ -319,7 +318,6 @@ class TestConnectRouteFunction(unittest.TestCase):
condition_subdomain=True)
self._callFUT(directive)
self.assertEqual(len(mapper.connections), 1)
- self.assertEqual(mapper.connections[0][0], ('a/b/c',))
self.assertEqual(mapper.connections[0][1],
{'requirements': {},
'_static':True,
@@ -335,7 +333,6 @@ class TestConnectRouteFunction(unittest.TestCase):
condition_function=foo)
self._callFUT(directive)
self.assertEqual(len(mapper.connections), 1)
- self.assertEqual(mapper.connections[0][0], ('a/b/c',))
self.assertEqual(mapper.connections[0][1],
{'requirements': {},
'_static':True,
@@ -349,7 +346,6 @@ class TestConnectRouteFunction(unittest.TestCase):
condition_method='GET')
self._callFUT(directive)
self.assertEqual(len(mapper.connections), 1)
- self.assertEqual(mapper.connections[0][0], ('a/b/c',))
self.assertEqual(mapper.connections[0][1],
{'requirements': {},
'_static':True,
@@ -359,11 +355,12 @@ class TestConnectRouteFunction(unittest.TestCase):
def test_subdomains(self):
mapper = self._registerRoutesMapper()
- directive = DummyRouteDirective(static=True, explicit=True,
+ directive = DummyRouteDirective(name='name',
+ static=True, explicit=True,
subdomains=['a', 'b'])
self._callFUT(directive)
self.assertEqual(len(mapper.connections), 1)
- self.assertEqual(mapper.connections[0][0], ('a/b/c',))
+ self.assertEqual(mapper.connections[0][0], ('name', 'path'))
self.assertEqual(mapper.connections[0][1],
{'requirements': {},
'_static':True,
@@ -371,7 +368,7 @@ class TestConnectRouteFunction(unittest.TestCase):
'conditions':{'sub_domain':['a', 'b']}
})
-class TestRouteGroupingContextDecorator(unittest.TestCase):
+class TestRoute(unittest.TestCase):
def setUp(self):
cleanUp()
@@ -382,33 +379,79 @@ class TestRouteGroupingContextDecorator(unittest.TestCase):
from repoze.bfg.zcml import Route
return Route
- def _makeOne(self, context, path, **kw):
- return self._getTargetClass()(context, path, **kw)
+ def _makeOne(self, context, path, name, view, **kw):
+ return self._getTargetClass()(context, path, name, view, **kw)
def test_defaults(self):
context = DummyContext()
- route = self._makeOne(context, 'abc')
+ view = Dummy()
+ route = self._makeOne(context, 'path', 'name', view)
+ self.assertEqual(route.path, 'path')
+ self.assertEqual(route.name, 'name')
+ self.assertEqual(route.view, view)
self.assertEqual(route.requirements, {})
- self.assertEqual(route.parent_member_name, None)
- self.assertEqual(route.parent_collection_name, None)
def test_parent_collection_name_missing(self):
context = DummyContext()
- self.assertRaises(ValueError, self._makeOne, context, 'abc',
+ view = Dummy()
+ from zope.configuration.exceptions import ConfigurationError
+ self.assertRaises(ConfigurationError, self._makeOne, context,
+ 'path', 'name', view,
parent_member_name='a')
def test_parent_collection_name_present(self):
context = DummyContext()
- route = self._makeOne(context, 'abc',
+ view = Dummy()
+ route = self._makeOne(context, 'path', 'name', view,
parent_member_name='a',
parent_collection_name='p')
self.assertEqual(route.parent_member_name, 'a')
self.assertEqual(route.parent_collection_name, 'p')
- def test_explicit_view_name(self):
+ def test_after(self):
+ from repoze.bfg.zcml import handler
+ from repoze.bfg.zcml import connect_route
+ from repoze.bfg.interfaces import IRoutesContext
+ from repoze.bfg.interfaces import IRequest
+ from repoze.bfg.interfaces import IView
+
context = DummyContext()
- route = self._makeOne(context, 'abc', view_name='def')
- self.assertEqual(route.view_name, 'def')
+ view = Dummy()
+ route = self._makeOne(context, 'path', 'name', view)
+ route.after()
+ actions = context.actions
+ self.assertEqual(len(actions), 2)
+
+ view_action = actions[0]
+ view_callable = view_action['callable']
+ view_discriminator = view_action['discriminator']
+ view_args = view_action['args']
+ self.assertEqual(view_callable, handler)
+ self.assertEqual(len(view_discriminator), 6)
+ self.assertEqual(view_discriminator[0], 'view')
+ self.assertEqual(view_discriminator[1], IRoutesContext)
+ self.assertEqual(view_discriminator[2],'name')
+ self.assertEqual(view_discriminator[3], IRequest)
+ self.assertEqual(view_discriminator[4], IView)
+ self.assertEqual(view_discriminator[5], True)
+ self.assertEqual(view_args, ('registerAdapter', view,
+ (IRoutesContext, IRequest), IView,
+ 'name', None))
+
+ route_action = actions[1]
+ route_callable = route_action['callable']
+ route_discriminator = route_action['discriminator']
+ route_args = route_action['args']
+ self.assertEqual(route_callable, connect_route)
+ self.assertEqual(len(route_discriminator), 7)
+ self.assertEqual(route_discriminator[0], 'route')
+ self.assertEqual(route_discriminator[1], 'path')
+ self.assertEqual(route_discriminator[2],'{}')
+ self.assertEqual(route_discriminator[3], None)
+ self.assertEqual(route_discriminator[4], None)
+ self.assertEqual(route_discriminator[5], None)
+ self.assertEqual(route_discriminator[6], None)
+ self.assertEqual(route_args, (route,))
class TestZCMLPickling(unittest.TestCase):
i = 0
@@ -760,6 +803,11 @@ class Dummy:
pass
class DummyRouteDirective:
+ path = 'path'
+ name = 'name'
+ view = None
+ factory = None
+ permission = None
encoding = None
static = False
minimize = False
@@ -775,11 +823,6 @@ class DummyRouteDirective:
condition_subdomain = None
condition_function = None
subdomains = None
- path = 'a/b/c'
- name = None
- view_name = ''
- factory = None
- provides = ()
def __init__(self, **kw):
if not 'requirements' in kw:
kw['requirements'] = {}
@@ -796,4 +839,3 @@ from zope.interface import Interface
class IDummy(Interface):
pass
-
diff --git a/repoze/bfg/urldispatch.py b/repoze/bfg/urldispatch.py
index ead8876de..2b6717891 100644
--- a/repoze/bfg/urldispatch.py
+++ b/repoze/bfg/urldispatch.py
@@ -31,85 +31,6 @@ deprecated('RoutesContext',
"DefaultRoutesContext')",
)
-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.
- Any view that claims it is 'for' the interface
- ``repoze.bfg.interfaces.IRoutesContext`` 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.
-
- .. warning:: This class is deprecated. As of :mod:`repoze.bfg`
- 0.6.3, you should use the ``<route.. >`` ZCML directive instead
- of manually creating a RoutesMapper. See :ref:`urldispatch_chapter`
- for more information.
- """
- 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', '/')
- self.mapper.environ = environ
- args = self.mapper.match(path)
- if isinstance(args, dict): # might be an empty dict
- context_factory = args.get('context_factory', _marker)
- if context_factory is _marker:
- context_factory = DefaultRoutesContext
- else:
- args = args.copy()
- del args['context_factory']
- 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
- context = context_factory(**args)
- alsoProvides(context, IRoutesContext)
- return context
-
- # 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. One differences exists: if the
- ``context_factory`` is passed in with a value as a keyword
- argument, this callable will be called when a model object
- representing the ``context`` for the request needs to be
- constructed. It will be called with the (all-keyword)
- arguments supplied by the Routes mapper's ``match`` method for
- this route, and should return an instance of a class. If
- ``context_factory`` is not supplied in this way for a route, a
- default context factory (the ``DefaultRoutesContext`` class)
- will be used. The interface
- ``repoze.bfg.interfaces.IRoutesContext`` will always be tacked
- on to the context instance in addition to whatever interfaces
- the context instance already supplies.
- """
-
- self.mapper.connect(*arg, **kw)
-
-deprecated('RoutesMapper',
- 'Usage of the ``RoutesMapper`` class is deprecated. As of '
- 'repoze.bfg 0.6.3, you should use the ``<route.. >`` ZCML '
- 'directive instead of manually creating a RoutesMapper.',
- )
-
class RoutesContextNotFound(object):
implements(IContextNotFound)
def __init__(self, msg):
@@ -172,7 +93,7 @@ class RoutesRootFactory(Mapper):
args = args.copy()
routepath = route.routepath
factory = route._factory
- if not factory:
+ if factory is None:
factory = DefaultRoutesContext
config = request_config()
config.mapper = self
@@ -189,9 +110,7 @@ class RoutesRootFactory(Mapper):
kw[k] = v
context = factory(**kw)
environ['wsgiorg.routing_args'] = ((), kw)
- provides = route._provides
- for iface in provides:
- alsoProvides(context, iface)
+ environ['bfg.route'] = route
alsoProvides(context, IRoutesContext)
return context
@@ -207,29 +126,12 @@ class RoutesModelTraverser(object):
self.context = context
def __call__(self, environ):
- # the traverser *wants* to get routing args from the environ
- # as of 0.6.5; the rest of this stuff is for backwards
- # compatibility
- try:
- # 0.6.5 +
- match = environ['wsgiorg.routing_args'][1]
- except KeyError:
- # <= 0.6.4
- match = self.context.__dict__
- try:
- view_name = match['view_name']
- except KeyError:
- # b/w compat < 0.6.3
- try:
- view_name = match['controller']
- except KeyError:
- view_name = ''
- try:
- subpath = match['subpath']
+ route = environ['bfg.route']
+ match = environ['wsgiorg.routing_args'][1]
+
+ subpath = match.get('subpath', [])
+ if subpath:
subpath = filter(None, subpath.split('/'))
- except KeyError:
- # b/w compat < 0.6.5
- subpath = []
if 'path_info' in match:
# this is stolen from routes.middleware; if the route map
@@ -245,7 +147,7 @@ class RoutesModelTraverser(object):
if environ['SCRIPT_NAME'].endswith('/'):
environ['SCRIPT_NAME'] = environ['SCRIPT_NAME'][:-1]
- return self.context, view_name, subpath, None, self.context, None
+ return self.context, route.name, subpath, None, self.context, None
class RoutesContextURL(object):
""" The IContextURL adapter used to generate URLs for a context
diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py
index 61b6e3738..16e6d7012 100644
--- a/repoze/bfg/zcml.py
+++ b/repoze/bfg/zcml.py
@@ -24,8 +24,10 @@ from zope.schema import TextLine
from repoze.bfg.interfaces import IRequest
from repoze.bfg.interfaces import IRoutesMapper
+from repoze.bfg.interfaces import IRoutesContext
from repoze.bfg.interfaces import IViewPermission
from repoze.bfg.interfaces import IView
+
from repoze.bfg.path import package_path
from repoze.bfg.security import ViewPermissionFactory
@@ -271,12 +273,11 @@ def route_requirement(context, attr, expr):
class IRouteDirective(Interface):
""" The interface for the ``route`` ZCML directive
"""
+ name = TextLine(title=u'name', required=True)
path = TextLine(title=u'path', required=True)
- name = TextLine(title=u'name', required=False)
- view_name = TextLine(title=u'view_name', required=False)
+ view = GlobalObject(title=u'view', required=True)
+ permission = TextLine(title=u'permission', required=False)
factory = GlobalObject(title=u'context factory', required=False)
- provides = Tokens(title=u'context interfaces', required=False,
- value_type=GlobalObject())
minimize = Bool(title=u'minimize', required=False)
encoding = TextLine(title=u'encoding', required=False)
static = Bool(title=u'static', required=False)
@@ -299,13 +300,8 @@ def connect_route(directive):
mapper = queryUtility(IRoutesMapper)
if mapper is None:
return
- args = []
- if directive.name:
- args.append(directive.name)
- args.append(directive.path)
+ args = [directive.name, directive.path]
kw = dict(requirements=directive.requirements)
- if directive.view_name:
- kw['view_name'] = directive.view_name
if directive.minimize:
kw['_minimize'] = True
if directive.explicit:
@@ -341,57 +337,55 @@ def connect_route(directive):
if directive.factory:
kw['_factory'] = directive.factory
- if directive.provides:
- kw['_provides'] = directive.provides
return mapper.connect(*args, **kw)
class Route(zope.configuration.config.GroupingContextDecorator):
""" Handle ``route`` ZCML directives
"""
-
- implements(zope.configuration.config.IConfigurationContext,IRouteDirective)
-
- def __init__(self, context, path, name=None, view_name='', factory=None,
- provides=(), minimize=True, encoding=None, static=False,
- filter=None, absolute=False, member_name=None,
- collection_name=None, condition_method=None,
- condition_subdomain=None, condition_function=None,
- parent_member_name=None, parent_collection_name=None,
- subdomains=None, explicit=False):
+ view = None
+ permission = None
+ factory = None
+ minimize = True
+ encoding = None
+ static = False
+ filter = None
+ absolute = False
+ member_name = None
+ collection_name = None
+ condition_method = None
+ condition_subdomain = None
+ condition_function = None
+ parent_member_name = None
+ parent_collection_name = None
+ subdomains = None
+ explicit = False
+
+ implements(zope.configuration.config.IConfigurationContext,
+ IRouteDirective)
+
+ def __init__(self, context, path, name, view, **kw):
+ self.validate(**kw)
+ self.requirements = {} # mutated by subdirectives
self.context = context
self.path = path
self.name = name
- self.view_name = view_name
- self.factory = factory
- self.provides = provides
- self.minimize = minimize
- self.encoding = encoding
- self.static = static
- self.filter = filter
- self.absolute = absolute
- self.member_name = member_name
- self.collection_name = collection_name
- self.condition_method= condition_method
- self.condition_subdomain = condition_subdomain
- self.condition_function = condition_function
- self.explicit = explicit
- self.subdomains = subdomains
- if parent_member_name is not None:
- if parent_collection_name is not None:
- self.parent_member_name = parent_member_name
- self.parent_collection_name = parent_collection_name
- else:
- raise ValueError(
+ self.view = view
+ self.__dict__.update(**kw)
+
+ def validate(self, **kw):
+ parent_member_name = kw.get('parent_member_name')
+ parent_collection_name = kw.get('parent_collection_name')
+ if parent_member_name or parent_collection_name:
+ if not (parent_member_name and parent_collection_name):
+ raise ConfigurationError(
'parent_member_name and parent_collection_name must be '
'specified together')
- else:
- self.parent_member_name = None
- self.parent_collection_name = None
- # added by subdirectives
- self.requirements = {}
def after(self):
+ view(self.context, self.permission, IRoutesContext, self.view,
+ self.name, None)
+
self.context.action(
discriminator = ('route', self.path, repr(self.requirements),
self.condition_method, self.condition_subdomain,
@@ -399,3 +393,4 @@ class Route(zope.configuration.config.GroupingContextDecorator):
callable = connect_route,
args = (self,),
)
+