summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO.txt9
-rw-r--r--docs/api/urldispatch.rst48
-rw-r--r--docs/narr/project.rst2
-rw-r--r--docs/narr/urldispatch.rst192
-rw-r--r--repoze/bfg/urldispatch.py1
5 files changed, 189 insertions, 63 deletions
diff --git a/TODO.txt b/TODO.txt
deleted file mode 100644
index 109b176e5..000000000
--- a/TODO.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-- mapply decorator.
-
-- URL-based dispatch should get first crack at a request.
-
-- z3c.pt needs to support common XML entities.
-
-
-
-
diff --git a/docs/api/urldispatch.rst b/docs/api/urldispatch.rst
index 40b22e5bc..2ebdd6de4 100644
--- a/docs/api/urldispatch.rst
+++ b/docs/api/urldispatch.rst
@@ -8,51 +8,3 @@
.. autoclass:: RoutesMapper
:members:
-An example of configuring a ``bfg:view`` stanza in ``configure.zcml``
-that maps a context found via :term:`Routes` URL dispatch to a view
-function is as follows:
-
-.. code-block:: xml
- :linenos:
-
- <bfg:view
- for="repoze.bfg.interfaces.IRoutesContext"
- view=".views.articles_view"
- name="articles"
- />
-
-All context objects found via Routes URL dispatch will provide the
-``IRoutesContext`` interface (attached dynamically). You might then
-configure the ``RoutesMapper`` like so:
-
-.. code-block:: python
- :linenos:
-
- def fallback_get_root(environ):
- return {} # the graph traversal root is empty in this example
-
- class Article(object):
- def __init__(self, **kw):
- self.__dict__update(kw)
-
- get_root = RoutesMapper(fallback_get_root)
- get_root.connect('archives/:article', controller='articles',
- context_factory=Article)
-
- import myapp
- from repoze.bfg.router import make_app
-
- app = make_app(get_root, myapp)
-
-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
-called with its :term:`context` as a instance of the ``Article``
-class. The ``Article`` instance will have attributes matching the
-keys and values in the Routes routing dictionary associated with the
-request.
-
-In this case in particular, when a user visits
-``/archives/something``, the context will be an instance of the
-Article class and it will have an ``article`` attribute with the value
-of ``something``.
diff --git a/docs/narr/project.rst b/docs/narr/project.rst
index 1dca10991..7360053dd 100644
--- a/docs/narr/project.rst
+++ b/docs/narr/project.rst
@@ -507,6 +507,8 @@ in the model, and the HTML given back to the browser.
module, which you can use to retrieve the template object without
rendering it at all, for additional control.
+.. _modelspy_project_section:
+
``models.py``
~~~~~~~~~~~~~
diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst
index 42413ca73..2053c1620 100644
--- a/docs/narr/urldispatch.rst
+++ b/docs/narr/urldispatch.rst
@@ -3,12 +3,192 @@
URL Dispatch
============
-It is more 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.
+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.
-Please see the :ref:`urldispatch_module` module API documentation for
-more information on using :term:`URL dispatch` with :mod:`repoze.bfg`.
+.. note:: In :term:`Routes` lingo, the code that it allows you to map
+ 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:`view`.
+ As a result, there is a bit of mental gynmastics you'll need
+ to do when dealing with Routes URL dispatch in bfg. In
+ general, whenever you see the name *controller* in the
+ context of :term:`Routes`, you should map that mentally to
+ the bfg term :term:`view`. *Actions* do not exist in
+ :mod:`repoze.bfg`: in other frameworks these may refer to
+ methods of a *controller class*, but since views in
+ :mod:`repoze.bfg` are simple callables (usually functions)
+ as opposed to classes, there is no direct concept mapping of
+ an action.
+It often makes a lot of sense to use :term:`URL dispatch` instead of
+:term:`traversal` in an application that has no natural hierarchy.
+For instance, if all the data in your application lives in a
+relational database, and that relational database has no
+self-referencing tables that form a natural hierarchy, URL dispatch is
+easier to use than traversal, and is often a more natural fit for
+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 defines a module named
+:mod:`repoze.bfg.urldispatch`. This module contains a class named
+:class:`RoutesMapper`. An Instance of this class is willing to act as
+a :mod:`repoze.bfg` ``get_root`` callable, and is willing to be
+configured with *route mappings* as necessary via its ``.connect``
+method.
+
+A ``get_root`` callable is a callable passed to the :mod:`repoze.bfg`
+framework by an application, allowing bfg to find the "root" object of
+a traversal graph. The :class:`RoutesMapper` is essentially willing
+to act as the "root callable". When it acts as such a callable, it is
+willing to check the requested URL against a *routes map*, and
+subsequently 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 URL matches, the
+:class:``RoutesMapper`` will fall back to calling a ``get_root``
+callable that is passed in to it at construction time, which allows
+your application to fall back to a different "root" (perhaps one based
+on traversal). By configuring a :class:`RoutesMapper` 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.
+
+Configuring a :class:`RoutesMapper` with individual routes is
+performed by creating an instance of a :class:`RoutesMapper`, and
+calling its ``.connect`` method with the same arguments you'd use if
+you were creating a route mapping using a "raw" :term:`Routes`
+``Mapper`` object. See `Setting up routes
+<http://routes.groovie.org/manual.html#setting-up-routes>`_ for
+examples of using a Routes ``Mapper`` object. When you are finished
+configuring it, you can pass it as a ``get_root`` callable to
+:mod:`repoze.bfg`.
+
+When you configure a :class:`RoutesMapper` with a route via
+``.connect``, you'll pass in the name of a ``controller`` as a keyword
+argument. This will be a string. The string should match the
+**name** of a :mod:`repoze.bfg` :term:`view` callable that is
+registered for the type ``repoze.bfg.interfaces.IRoutesContext`` (via
+a ZCML directive, see :ref:`views_chapter` for more information about
+registering bfg views). When a URL is matched, this view will be
+called with a :term:`context` manufactured "on the fly" by the
+:class:`RoutesMapper`. The context object will have attributes which
+match all of the :term:`Routes` matching arguments returned by the
+mapper.
+
+Example 1
+---------
+
+Below is an example of configuring a :class:`RoutesMapper` for usage
+as a ``get_root`` callback.
+
+.. code-block:: python
+ :linenos:
+
+ from repoze.bfg.urldispatch import RoutesMapper
+
+ def fallback_get_root(environ):
+ return {}
+
+ root = RoutesMapper(fallback_get_root)
+ root.connect('ideas/:idea', controller='ideas')
+ root.connect('users/:user', controller='users')
+ root.connect('tags/:tag', controller='tags')
+
+The above configuration will allow the mapper to service URLs in the forms::
+
+ /ideas/<ideaname>
+ /users/<username>
+ /tags/<tagname>
+
+If this mapper is used as a ``get_root`` callback, 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 interfaces type or name.
+
+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 ``context_factory`` argument to the
+mapper's connect method for a particular route. The
+``context_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.
+
+If no route matches in the above configuration, the routes mapper will
+call the "fallback" ``get_root`` callable provided to it above.
+
+Example 2
+---------
+
+An example of configuring a ``bfg:view`` stanza in ``configure.zcml``
+that maps a context found via :term:`Routes` URL dispatch to a view
+function is as follows:
+
+.. code-block:: xml
+ :linenos:
+
+ <bfg:view
+ for="repoze.bfg.interfaces.IRoutesContext"
+ view=".views.articles_view"
+ name="articles"
+ />
+
+All context objects found via Routes URL dispatch will provide the
+``IRoutesContext`` interface (attached dynamically). You might then
+configure the ``RoutesMapper`` like so:
+
+.. code-block:: python
+ :linenos:
+
+ def fallback_get_root(environ):
+ return {} # the graph traversal root is empty in this example
+
+ class Article(object):
+ def __init__(self, **kw):
+ self.__dict__update(kw)
+
+ get_root = RoutesMapper(fallback_get_root)
+ get_root.connect('archives/:article', controller='articles',
+ context_factory=Article)
+
+ import myapp
+ from repoze.bfg.router import make_app
+
+ app = make_app(get_root, myapp)
+
+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
+called with its :term:`context` as a instance of the ``Article``
+class. The ``Article`` instance will have attributes matching the
+keys and values in the Routes routing dictionary associated with the
+request.
+
+In this case in particular, when a user visits
+``/archives/something``, the context will be an instance of the
+Article class and it will have an ``article`` attribute with the value
+of ``something``.
+
+Further Documentation and Examples
+----------------------------------
+
+URL-dispatch related API documentation is available in the
+:ref:`urldispatch_module` .
+
+The `repoze.shootout <http://svn.repoze.org/repoze.shootout/trunk/>`_
+application uses URL dispatch to serve its "ideas", "users" and "tags"
+pages.
diff --git a/repoze/bfg/urldispatch.py b/repoze/bfg/urldispatch.py
index 7f81dbd30..0be937ab0 100644
--- a/repoze/bfg/urldispatch.py
+++ b/repoze/bfg/urldispatch.py
@@ -12,6 +12,7 @@ from repoze.bfg.interfaces import ITraverser
_marker = ()
class RoutesContext(object):
+ implements(IRoutesContext)
def __init__(self, **kw):
self.__dict__.update(kw)