From 39b8b920c8018dc4d124cba72794da1dcc925cce Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 31 Jul 2008 07:47:43 +0000 Subject: More balance between URL dispatch and traversal. --- docs/glossary.rst | 3 ++ docs/index.rst | 2 + docs/narr/traversal.rst | 127 +++++++++++++++------------------------------- docs/narr/urldispatch.rst | 14 +++++ docs/narr/urlmapping.rst | 99 ++++++++++++++++++++++++++++++++++++ 5 files changed, 160 insertions(+), 85 deletions(-) create mode 100644 docs/narr/urldispatch.rst create mode 100644 docs/narr/urlmapping.rst (limited to 'docs') diff --git a/docs/glossary.rst b/docs/glossary.rst index 385cc7f24..f1ccf1575 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -159,6 +159,9 @@ Glossary Zope `The Z Object Publishing Framework `_. The granddaddy of Python web frameworks. + ZODB + `Zope Object Database `_, a + persistent Python object store. WebOb `WebOb `_ is a WSGI request/response library created by Ian Bicking. diff --git a/docs/index.rst b/docs/index.rst index 63f8b96e4..3f20d0797 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -25,7 +25,9 @@ Narrative documentation in chapter form explaining how to use narr/introduction narr/install narr/project + narr/urlmapping narr/traversal + narr/urldispatch narr/views narr/templates narr/models diff --git a/docs/narr/traversal.rst b/docs/narr/traversal.rst index 673f94927..f03a22c9e 100644 --- a/docs/narr/traversal.rst +++ b/docs/narr/traversal.rst @@ -3,100 +3,57 @@ Traversal ========= -In many popular web frameworks, :term:`URL dispatch` is used to -associate a particular URL with a bit of code (known somewhat -ambiguously as a "controller" or :term:`view` depending upon the -particular vocabulary religion to which you subscribe). These systems -allow the developer to create "urlconfs" or "routes" to -controller/view Python code using pattern matching against URL -components. Examples: `Django's URL dispatcher -`_ and the -:term:`Routes` URL mapping system. - -:mod:`repoze.bfg` supports :term:`URL dispatch` via :term:`Routes`. -See the :ref:`urldispatch_module` for more information about using URL -dispatch. - -By default, however, :mod:`repoze.bfg` does not use URL dispatch to -map URLs to code. Instead, it maps URLs to code slightly differently, -using object graph :term:`traversal`. The venerable Zope and CherryPy -web frameworks offer graph-traversal-based URL dispatch. -Graph-traversal based dispatching is useful if you like the URL to -represent an arbitrary hierarchy of potentially heterogeneous items. - -:term:`URL dispatch` can easily handle URLs such as -``http://example.com/members/Chris``, where it's assumed that each -item "below" ``members`` in the URL represents a member in the system. -You just match everything "below" ``members`` to a particular view. -They are not very good, however, at inferring the difference between -sets of URLs such as:: - - http://example.com/members/Chris/document - http://example.com/members/Chris/stuff/page - -...wherein you'd like the ``document`` in the first URL to represent a -PDF document, and ``/stuff/page`` in the second to represent an -*OpenOffice* document in a "stuff" folder. It takes more pattern -matching assertions to be able to make URLs like these work in -URL-dispatch based systems, and some assertions just aren't possible. -For example, URL-dispatch based systems don't deal very well with URLs -that represent arbitrary-depth hierarchies. - -Graph :term:`traversal` works well if you need to divine meaning out -of these types of "ambiguous" URLs and URLs that represent -arbitrary-depth hierarchies. Each URL segment represents a single -traversal through an edge of the graph. So a URL like -``http://example.com/a/b/c`` can be thought of as a graph traversal on -the example.com site through the edges ``a``, ``b``, and ``c``. - -Finally, if you're willing to treat your application models as a graph -that can be traversed, it also becomes trivial to provide "row-level -security" (in common relational parlance): you just attach a security -declaration to each instance in the graph. This is not as easy in -frameworks that use URL-based dispatch. - -Graph traversal is materially more complex than URL-based dispatch, -however, if only because it requires the construction and maintenance -of a graph, and it requires the developer to think about mapping URLs -to code in terms of traversing the graph. (How's *that* for -self-referential! ;-) ) That said, for developers comfortable with -:term:`Zope` or comfortable with hierarchical data stores like *ZODB* -or a filesystem, mapping a URL to a graph traversal is a natural way -to think about creating a web application. - -In essence, the choice to use graph traversal vs. URL dispatch is -largely religious in some sense. Graph traversal dispatch probably -just doesn't make any sense when you possess completely "square" data -stored in a relational database. However, when you have a -hierarchical data store, it can provide advantages over using -URL-based dispatch. - -:mod:`repoze.bfg` provides support for both approaches. Graph -traversal is described in detail below. +The :mod:`repoze.bfg` *Router* parses the URL associated with the +request and traverses the graph based on path segments in the URL. +Based on these path segments, :mod:`repoze.bfg` traverses the *model +graph* in order to find a :term:`context`. It then attempts to find a +:term:`view` based on the *type* of the context (specified by an +:term:`interface`). If :mod:`repoze.bfg` finds a :term:`view` for the +context, it calls it and returns a response to the user. The Model Graph --------------- +When your application uses :term:`traversal` to resolve URLs to code, +your application must supply a *model graph* to :mod:`repoze.bfg`. + Users interact with your :mod:`repoze.bfg` -based application via a -"router", which is itself a WSGI application. At system startup time, -the router is configured with a root object from which all traversal -will begin. The root object is usually a mapping object, such as a -Python dictionary. Usually the root is a *container* node, and thus -contains other items. In fact, all items contained in the graph are -either *leaf* nodes (these have no ``__getitem__``) or *container* -nodes (these do have a ``__getitem__``). +*router*, which is just a fancy :term:`WSGI` application. At system +startup time, the router is configured with a single object which +represents the root of the model graph. All traversal will begin at +this root object. The root object is usually a *mapping* object (such +as a Python dictionary). Items contained within the graph are analogous to the concept of :term:`model` objects used by many other frameworks (and :mod:`repoze.bfg` refers to them as models, as well). They are -typically instances of Python classes. Each containerish instance is -willing to return a child or raise a ``KeyError`` based on a name -passed to its ``__getitem__``. No leaf-level instance is required to -have a ``__getitem__``. - -:mod:`repoze.bfg` traverses the model graph in order to find a -:term:`context`. It then attempts to find a :term`view` based on the -type (specified by an :term:`interface`) of the context. +typically instances of Python classes. + +The model graph consists of *container* nodes and *leaf* nodes. There +is only one difference between *container* node and a *leaf* node: +*container* nodes a ``__getitem__`` method while *leaf* nodes do not. +The ``__getitem__`` method was chosen as the signifying difference +between the two types of nodes because the presence of this method is +how Python itself typically determines whether an object is +"containerish" or not. + +A container node is presumed to be willing to return a child node or +raise a ``KeyError`` based on a name passed to its ``__getitem__``. + +No leaf-level instance is required to have a ``__getitem__``. If +leaf-level instances happen to have a ``__getitem__`` (through some +historical inequity), you should subclass these node types and cause +their ``__getitem__`` methods to simply raise a ``KeyError``. Or just +disuse them and think up another strategy. + +Usually, the traversal root is a *container* node, and as such it +contains other nodes. However, it doesn't *need* to be a container. +Your model graph can be as shallow or as deep as you require. + +Traversal "stops" when :mod:`repoze.bfg` either reaches a leaf level +model instance in your object graph or when the path segments implied +by the URL "run out". The object that traversal "stops on" becomes +the :term:`context`. How :mod:`repoze.bfg` Processes a Request Using Traversal --------------------------------------------------------- diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst new file mode 100644 index 000000000..42413ca73 --- /dev/null +++ b/docs/narr/urldispatch.rst @@ -0,0 +1,14 @@ +.. _urldispatch_chapter: + +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. + +Please see the :ref:`urldispatch_module` module API documentation for +more information on using :term:`URL dispatch` with :mod:`repoze.bfg`. + + diff --git a/docs/narr/urlmapping.rst b/docs/narr/urlmapping.rst new file mode 100644 index 000000000..3c74d04d9 --- /dev/null +++ b/docs/narr/urlmapping.rst @@ -0,0 +1,99 @@ +.. _url_mapping_chapter: + +Mapping URLs to Code +==================== + +Many popular web frameworks today use :term:`URL dispatch` to +associate a particular URL with a bit of code (known somewhat +ambiguously as a "controller" or :term:`view` depending upon the +particular vocabulary religion to which you subscribe). These systems +allow the developer to create "urlconfs" or "routes" to +controller/view Python code using pattern matching against URL +components. Examples: `Django's URL dispatcher +`_ and the +:term:`Routes` URL mapping system. + +:mod:`repoze.bfg` supports :term:`URL dispatch` via :term:`Routes`. +:term:`URL dispatch` is convenient and straightforward. When you +limit your application to using URL dispatch, you know every URL that +your application might generate or respond to, and all the URL +matching elements are listed in a single place. + +Like :term:`Zope`, :mod:`repoze.bfg`, in contrast to URL dispatch, can +also map URLs to code slightly differently, by using using object +graph :term:`traversal`. Graph-traversal based dispatching is useful +if you like your URLs to represent an arbitrary hierarchy of +potentially heterogeneous items, or if you need to attach +"instance-level" security (akin to "row-level" security in relational +parlance) declarations to :term:`model` instances. + +Differences Between Traversal and URL Dispatch +---------------------------------------------- + +:term:`URL dispatch` can easily handle URLs such as +``http://example.com/members/Chris``, where it's assumed that each +item "below" ``members`` in the URL represents a member in the system. +You just match everything "below" ``members`` to a particular view. + +For example, you might configure a :term:`Routes` URL map to match +against the following URL patterns:: + + archives/:year/:month/:day + members/:membername + +In this configuration, there are exactly two types of URLs that will +match views in your application: ones that start with ``/archives`` +and have subsequent path elements that represent a year, month, and +day. And ones that start with ``/members`` which are followed by a +path segment containing a member's name. This is very simple. + +:term:`URL dispatch` is not very good, however, at inferring the +difference between sets of URLs such as:: + + http://example.com/members/Chris/document + http://example.com/members/Chris/stuff/page + +...wherein you'd like the ``document`` in the first URL to represent a +PDF document, and ``/stuff/page`` in the second to represent an +*OpenOffice* document in a "stuff" folder. It takes more pattern +matching assertions to be able to make URLs like these work in +URL-dispatch based systems, and some assertions just aren't possible. +For example, URL-dispatch based systems don't deal very well with URLs +that represent arbitrary-depth hierarchies. + +Graph :term:`traversal` works well if you need to divine meaning out +of these types of "ambiguous" URLs and URLs that represent +arbitrary-depth hierarchies. Each URL segment represents a single +traversal through an edge of the graph. So a URL like +``http://example.com/a/b/c`` can be thought of as a graph traversal on +the ``example.com`` site through the edges ``a``, ``b``, and ``c``. + +If you're willing to treat your application models as a graph that can +be traversed, it also becomes easy to provide "row-level security" (in +common relational parlance): you just attach a security declaration to +each instance in the graph. This is not as easy in frameworks that +use URL-based dispatch. + +Graph traversal is materially more complex than URL-based dispatch, +however, if only because it requires the construction and maintenance +of a graph, and it requires the developer to think about mapping URLs +to code in terms of traversing the graph. (How's *that* for +self-referential! ;-) ) + +In essence, the choice to use graph traversal vs. URL dispatch is +largely religious in some sense. Graph traversal dispatch probably +just doesn't make any sense when you possess completely "square" data +stored in a relational database. However, when you have a +hierarchical data store, it can provide advantages over using +URL-based dispatch. + +:mod:`repoze.bfg` provides support for both approaches. You can use +either as you see fit. + +.. note:: + + Most existng :mod:`repoze.bfg` applications use :term:`traversal` to + map URLs to code. This is mostly due to the :term:`Zope` heritage + of :mod:`repoze.bfg` and because it aids applications that require + highly granular declarative security assertions. + -- cgit v1.2.3