summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-01-19 06:53:28 +0000
committerChris McDonough <chrism@agendaless.com>2009-01-19 06:53:28 +0000
commit220715124566ea32331352dff7a677ff4be780bc (patch)
treead07cd78b20f76e268f27f29262dcae97b392743
parent1d4c6dc61a7b1033f0cf0a02bd8e3503c41f7432 (diff)
downloadpyramid-220715124566ea32331352dff7a677ff4be780bc.tar.gz
pyramid-220715124566ea32331352dff7a677ff4be780bc.tar.bz2
pyramid-220715124566ea32331352dff7a677ff4be780bc.zip
view_name attr.
-rw-r--r--docs/narr/urldispatch.rst181
-rw-r--r--docs/narr/views.rst4
-rw-r--r--repoze/bfg/tests/routesapp/configure.zcml5
-rw-r--r--repoze/bfg/tests/test_zcml.py11
-rw-r--r--repoze/bfg/zcml.py12
5 files changed, 149 insertions, 64 deletions
diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst
index ef445427c..13fedcd98 100644
--- a/docs/narr/urldispatch.rst
+++ b/docs/narr/urldispatch.rst
@@ -3,21 +3,25 @@
URL Dispatch
============
-It is common for :mod:`repoze.bfg` users to rely on :term:`traversal`
-to map URLs to code. However, :mod:`repoze.bfg` can 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.
-
-.. note:: In :term:`Routes` lingo, the code that it allows you to map
+It is common for :mod:`repoze.bfg` developers to rely on
+:term:`traversal` to map URLs to code. However, :mod:`repoze.bfg` can
+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.
+
+.. 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
- is found, "normal" :mod:`repoze.bfg` :term:`view` lookup
- will be done using the context and the view name.
+ 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.
It often makes a lot of sense to use :term:`URL dispatch` instead of
:term:`traversal` in an application that has no natural hierarchy.
@@ -30,40 +34,40 @@ creating an application that maniplates "flat" data.
Concept and Usage
-----------------
-The urldispatch features of :mod:`repoze.bfg` allow you to coopt
-natural :term:`traversal`, allowing a :term:`Routes` "mapper" object
-to have the "first crack" at resolving a given URL, allowing the
-framework to fall back to traversal as necessary.
-
-To this end, the :mod:`repoze.bfg` framework allows you to inject
-``route` ZCML directives into your application's ``configure.zcml``
-file. These directives have much the same job as imperatively using
-the ``.connect`` method of a routes Mapper object, with some
-BFG-specific behavior.
-
-When any ``route`` ZCML directive is used, BFG wraps the "default"
-"root factory" (aka ``get_root``) in a special ``RoutesRootFactory``
-instance. This then acts as the root factory (a callable). When it
-acts as such a callable, it is willing to check the requested URL
-against a *routes map* to find the :term:`context` and the
-:term:`view name`. Subsequently, BFG will look up and call a
-:mod:`repoze.bfg` view with the information it finds within a
-particular route, if any configured route matches the currently
-requested URL. If no route matches the configured routes,
-:mod:`repoze.bfg` will fail over to calling the ``get_root`` callable
-passed to the application in it's ``make_app`` function. By
-configuring your ZCML ``route`` statements appropriately, you can mix
-and match URL dispatch and traversal in this way.
+The URL dispatch features of :mod:`repoze.bfg` allow you to either
+augment or replace :term:`traversal`, allowing URL dispatch to have
+the "first crack" (and potentially the *only* crack) at resolving a
+given URL to :term:`context` and :term:`view name`.
+
+To allow for URL dispatch to be used, the :mod:`repoze.bfg` framework
+allows you to inject ``route`` ZCML directives into your application's
+``configure.zcml`` file.
+
+.. note:: Each ZCML ``route`` statement equates to a call to the
+ :term:`Routes` ``Mapper`` object's ``connect`` method. See
+ `Setting up routes
+ <http://routes.groovie.org/manual.html#setting-up-routes>`_
+ for examples of using a Routes ``Mapper`` object outside of
+ :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" (aka ``get_root``) in a special ``RoutesRootFactory``
+instance. This 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 ``get_root`` 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.
.. note:: See :ref:`modelspy_project_section` for an example of a
simple ``get_root`` callable that will use traversal.
-Each ZCML ``route``statement equals a call to the term:`Routes`
-``Mapper`` object's ``connect`` method. See `Setting up routes
-<http://routes.groovie.org/manual.html#setting-up-routes>`_ for
-examples of using a Routes ``Mapper`` object outside of
-:mod:`repoze.bfg`.
-
Example 1
---------
@@ -96,30 +100,76 @@ in these forms:
/tags/<tagname>
When a URL matches the pattern ``/ideas/<ideaname>``, the view
-registered with the name 'ideas' for the interface
+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 interfaces type or
-name.
+will be raised if no view can be found with that interface type and
+view name combination.
The context object passed to a view found as the result of URL
-dispatch will be an instance of the
-``repoze.bfg.urldispatch.RoutesContext`` object. You can override
-this behavior by passing in a ``factory`` argument to the ZCML
-directive for a particular route. The ``factory`` should be a
+dispatch will by default be an instance of the
+``repoze.bfg.urldispatch.DefaultRoutesContext`` object. You can
+override this behavior by passing in a ``factory`` argument to the
+ZCML directive for a particular route. The ``factory`` should be a
callable that accepts arbitrary keyword arguments and returns an
instance of a class that will be the context used by the view.
-The context object will be decorated by default with the
-``repoze.bfg.interfaces.IRoutesContext`` 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 interfaces.
+An example of using a route with a factory:
+
+.. code-block:: xml
+ :linenos:
+
+ <route
+ path="ideas/:idea"
+ 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.
+
+.. 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.
+
+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.
If no route matches in the above configuration, :mod:`repoze.bfg` will
call the "fallback" ``get_root`` callable provided to it during
``make_app`. If the "fallback" ``get_root`` is None, a ``NotFound``
error will be raised when no route matches.
+.. note:: See :ref:`using_model_interfaces` for more information about
+ how views are found when interfaces are attached to a
+ context. You can also map classes to views; interfaces are
+ not used then.
+
Example 2
---------
@@ -169,6 +219,29 @@ 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 :ref:`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.
+
Using :mod:`repoze.bfg` Security With URL Dispatch
--------------------------------------------------
@@ -204,8 +277,8 @@ is ``1``, :mod:`repoze.bfg` will generate an ``Article``
the ``view`` permission. Obviously you can do more generic things
that inspect the routes match dict to see if the ``article`` argument
matches a particular string; our sample ``article_factory`` function
-is not very ambitious. It could have just as well been done in the
-class' constructor, too.
+is not very ambitious. Its job could have just as well been done in
+the ``Article`` class' constructor, too.
.. note:: See :ref:`security_chapter` for more information about
:mod:`repoze.bfg` security and ACLs.
diff --git a/docs/narr/views.rst b/docs/narr/views.rst
index 5e911fd7a..edc7bf563 100644
--- a/docs/narr/views.rst
+++ b/docs/narr/views.rst
@@ -84,7 +84,7 @@ The above maps the ``.views.hello_world`` view function to
Python class represented by ``.models.Hello`` when the *view name* is
``hello.html``.
-.. note:: Values prefixed with a period (``.``)for the ``for`` and
+.. note:: Values prefixed with a period (``.``) for the ``for`` and
``view`` attributes of a ``view`` (such as those above) mean
"relative to the Python package directory in which this
:term:`ZCML` file is stored". So if the above ``view``
@@ -262,6 +262,8 @@ name will be ``my_view``, registered for models with the
no permission, registered against requests which implement the default
``IRequest`` interface.
+.. _using_model_interfaces:
+
Using Model Interfaces
----------------------
diff --git a/repoze/bfg/tests/routesapp/configure.zcml b/repoze/bfg/tests/routesapp/configure.zcml
index acd1d9d9e..17653615a 100644
--- a/repoze/bfg/tests/routesapp/configure.zcml
+++ b/repoze/bfg/tests/routesapp/configure.zcml
@@ -3,8 +3,9 @@
<include package="repoze.bfg.includes" />
<route
- path=":id/:view_name"
- provides=".models.IFixture"/>
+ path=":id"
+ provides=".models.IFixture"
+ view_name="default"/>
<view
name="default"
diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py
index a2653f262..50d8a468a 100644
--- a/repoze/bfg/tests/test_zcml.py
+++ b/repoze/bfg/tests/test_zcml.py
@@ -290,7 +290,7 @@ 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])
+ factory=foo, provides=[IDummy], view_name='def')
self._callFUT(directive)
self.assertEqual(len(mapper.connections), 1)
self.assertEqual(mapper.connections[0][0], ('a/b/c',))
@@ -310,6 +310,7 @@ class TestConnectRouteFunction(unittest.TestCase):
'conditions':c,
'_factory':foo,
'_provides':[IDummy],
+ 'view_name':'def',
})
def test_condition_subdomain_true(self):
@@ -403,8 +404,11 @@ class TestRouteGroupingContextDecorator(unittest.TestCase):
parent_collection_name='p')
self.assertEqual(route.parent_member_name, 'a')
self.assertEqual(route.parent_collection_name, 'p')
-
-
+
+ def test_explicit_view_name(self):
+ context = DummyContext()
+ route = self._makeOne(context, 'abc', view_name='def')
+ self.assertEqual(route.view_name, 'def')
class TestZCMLPickling(unittest.TestCase):
i = 0
@@ -760,6 +764,7 @@ class DummyRouteDirective:
subdomains = None
path = 'a/b/c'
name = None
+ view_name = ''
factory = None
provides = ()
def __init__(self, **kw):
diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py
index 463090146..a0aa613da 100644
--- a/repoze/bfg/zcml.py
+++ b/repoze/bfg/zcml.py
@@ -273,6 +273,7 @@ class IRouteDirective(Interface):
"""
path = TextLine(title=u'path', required=True)
name = TextLine(title=u'name', required=False)
+ view_name = TextLine(title=u'view_name', required=False)
factory = GlobalObject(title=u'context factory', required=False)
provides = Tokens(title=u'context interfaces', required=False,
value_type=GlobalObject())
@@ -303,6 +304,8 @@ def connect_route(directive):
args.append(directive.name)
args.append(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:
@@ -349,16 +352,17 @@ class Route(zope.configuration.config.GroupingContextDecorator):
implements(zope.configuration.config.IConfigurationContext,IRouteDirective)
- def __init__(self, context, path, name=None, factory=None,
- provides=(), minimize=True, encoding=None,
- static=False, filter=None, absolute=False,
- member_name=None, collection_name=None, condition_method=None,
+ 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):
self.context = context
self.path = path
self.name = name
+ self.view_name = view_name
self.factory = factory
self.provides = provides
self.minimize = minimize