diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/narr/contextfinding.rst | 71 | ||||
| -rw-r--r-- | docs/narr/hybrid.rst | 120 |
2 files changed, 90 insertions, 101 deletions
diff --git a/docs/narr/contextfinding.rst b/docs/narr/contextfinding.rst index 491113e62..74d9e3cc5 100644 --- a/docs/narr/contextfinding.rst +++ b/docs/narr/contextfinding.rst @@ -70,57 +70,40 @@ describe how view lookup interacts with context finding. Should I Use Traversal or URL Dispatch for Context Finding? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -: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 -:term:`view callable`. For example, you might configure URL dispatch -within :mod:`repoze.bfg` to match against the following URL patterns: - -.. code-block:: text - - members/:membername - archives/:year/:month/:day +:term:`URL dispatch` is very straightforward. When you limit your +application to using URL dispatch, you know every URL that your +application might generate or respond to, all the URL matching +elements are listed in a single place, and you needn't think about +:term:`context finding` or :term:`view lookup` at all. -In this configuration, there will be exactly two types of URLs that -will be meaningful to your application: URLs that start with -``/members`` which are followed by a path segment containing a -member's name. And URLs that start with ``/archives`` and have -subsequent path elements that represent a year, month, and day. Each -route pattern will be mapped to a specific :term:`view callable`. - -URL dispatch is very 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. +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 single member in some +system. You just match everything "below" ``members`` to a particular +:term:`view callable`, e.g. ``/members/:memberid``. -URL dispatch is not very good, however, at inferring the difference -between sets of URLs such as these: +However, URL dispatch is not very convenient if you'd like your URLs +to represent an arbitrary hierarchy. For example, if you need to +infer the difference between sets of URLs such as these, where the +``document`` in the first URL represents a PDF document, and +``/stuff/page`` in the second represents an OpenOffice document in a +"stuff" folder. .. code-block:: text http://example.com/members/Chris/document http://example.com/members/Chris/stuff/page -If you'd like the ``document`` in the first URL above to represent a -PDF document, and ``/stuff/page`` in the second to represent an -OpenOffice document in a "stuff" folder, it's hard to express this -using URL dispatch. It takes more pattern matching assertions to be -able to make hierarchies like these work in URL-dispatch based -systems, and some assertions just aren't possible. Essentially, -URL-dispatch based systems just don't deal very well with URLs that -represent arbitrary-depth hierarchies. - -However, the other URL mapping mode supported by :mod:`repoze.bfg`, -named :term:`traversal`, *does* work well for URLs that represent -arbitrary-depth hierarchies. When traversal is used, each URL segment -represents a single traversal step through an edge of a 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``. Since the path segments that compose a URL are addressed -separately, it becomes very easy to form URLs that represent arbitrary -depth hierarchies in a system that uses traversal. +It takes more pattern matching assertions to be able to make +hierarchies work in URL-dispatch based systems, and some assertions +just aren't possible. Essentially, URL-dispatch based systems just +don't deal very well with URLs that represent arbitrary-depth +hierarchies. +But :term:`traversal`, *does* work well for URLs that represent +arbitrary-depth hierarchies. Since the path segments that compose a +URL are addressed separately, it becomes very easy to form URLs that +represent arbitrary depth hierarchies in a system that uses traversal. When you're willing to treat your application models as a graph that can be traversed, it also becomes easy to provide "instance-level security": you just attach a security declaration to each instance in @@ -136,6 +119,4 @@ hierarchical data store, using traversal can provide significant advantages over using URL-based dispatch. Since :mod:`repoze.bfg` provides support for both approaches, you can -use either as you see fit; you can even combine them together if -necessary. - +use either exclusively or combine them as you see fit. diff --git a/docs/narr/hybrid.rst b/docs/narr/hybrid.rst index f94df725a..92e6428b5 100644 --- a/docs/narr/hybrid.rst +++ b/docs/narr/hybrid.rst @@ -8,13 +8,23 @@ incompatible) concepts of :term:`traversal` and :term:`url dispatch`. When you write *most* :mod:`repoze.bfg` applications, you'll be using either one or the other concept, but not both, to resolve URLs to -:term:`view` callables. However, for some problems, it's useful to -use both traversal *and* URL dispatch within the same application. +:term:`view` callables. However, to solve some problems, it's useful +to use both traversal *and* URL dispatch within the same application. :mod:`repoze.bfg` makes this possible via *hybrid* applications. -.. warning:: Creating applications that use hybrid-mode features of - :mod:`repoze.bfg` is a advanced topic that exposes non-trivial - corner cases. Don't use it unless you must. +.. warning:: + + Creating an application that uses hybrid-mode features of + :mod:`repoze.bfg` exposes non-trivial corner cases. Reasoning + about a "hybrid" URL dispatch + traversal model can be difficult + because the combination of the two concepts seems to fall outside + the sweet spot of `the magical number seven plus or minus 2 + <http://en.wikipedia.org/wiki/The_Magical_Number_Seven,_Plus_or_Minus_Two>`_. + To reason successfully about using URL dispatch and traversal + together, you need to understand 1) URL pattern matching, 2) root + factories and 3) the traversal algorithm, and the interactions + between all of them. Therefore, we don't recommend creating an + application that relies on hybrid behavior unless you must. The Schism ---------- @@ -46,7 +56,7 @@ to code will often have declarations like this within :term:`ZCML`: view=".views.bazbuz" /> -In other words, each :term:`route` typically corresponds with a single +In other words, each :term:`route` typically corresponds to a single view callable, and when that route is matched during a request, the view callable attached to it is invoked. @@ -56,8 +66,8 @@ type/name triad: - the context :term:`interface` ``None``, implying any context. -- A :term:`request type` interface that inherits from - :class:`repoze.bfg.interfaces.IRequest` *and* a +- Two :term:`request type` interfaces are attached to the request: the + :class:`repoze.bfg.interfaces.IRequest` interface and a dynamically-constructed route-statement-specific :term:`interface`. - the empty string as the :term:`view name`, implying the default @@ -66,18 +76,20 @@ type/name triad: This usually ensures that the named view will only be called when the route it's attached to actually matches. -Typically, applications that use only URL dispatch won't have any -``<view>`` directives in ZCML and will not have any calls to -:meth:`repoze.bfg.configuration.Configurator.add_view` in their -startup code. +Typically, an application that uses only URL dispatch won't perform +any view configuration in ZCML and won't have any calls to +:meth:`repoze.bfg.configuration.Configurator.add_view` in its startup +code. Traversal Only ~~~~~~~~~~~~~~ An application that uses :term:`traversal` exclusively to map URLs to -code just won't have any ``<route>`` declarations or calls to the -:meth:`repoze.bfg.configuration.Configurator.add_route`. Instead, its -view configuration will imply declarations that look like this: +code just won't have any ZCML ``<route>`` declarations nor will it +make any calls to the +:meth:`repoze.bfg.configuration.Configurator.add_route` method. +Instead, its view configuration will imply declarations that look like +this: .. code-block:: xml @@ -92,7 +104,7 @@ view configuration will imply declarations that look like this: /> "Under the hood", the above view statements register a view using the -following context/request/name triad: +following context/request/name :term:`triad`: - The :term:`context` interface ``None`` @@ -111,20 +123,10 @@ The ``.views.foobar`` view callable above will be called when the URL Hybrid Applications ------------------- -So far we've seen that *either* traversal or url dispatch to create a -:mod:`repoze.bfg` application. However, it is possible to combine the -competing concepts of traversal and url dispatch to resolve URLs to -code within the same application. - -Reasoning about a "hybrid" URL dispatch + traversal model can be -difficult because the combination of the two concepts seems to fall -outside the sweet spot of `the magical number seven plus or minus 2 -<http://en.wikipedia.org/wiki/The_Magical_Number_Seven,_Plus_or_Minus_Two>`_. -To reason successfully about using URL dispatch and traversal -together, you need to understand 1) URL pattern matching, 2) root -factories and 3) the traversal algorithm, and the interactions between -all of them. Therefore, use of this pattern is not recommended unless -you *really* need to use it. +We've seen that *either* traversal or url dispatch can be used to +create a :mod:`repoze.bfg` application. However, it is possible to +combine the competing concepts of traversal and url dispatch to +resolve URLs to code within the same application. Understanding how hybrid mode works requires a little "inside baseball" knowledge of how :mod:`repoze.bfg` works. No matter whether @@ -133,14 +135,18 @@ uses the :term:`Zope Component Architecture` under the hood to dispatch a request to a :term:`view callable`. In Zope Component Architecture-speak, a view callable is a "multi adapter" registered for a :term:`context` type and a :term:`request` type as well as a -particular :term:`view name`, aka a "triad". When a request is -generated and a :term:`router` performs its logic, it locates these -three values. These three values are fed to the :term:`application -registry` as a query to find "the best" view callable. +particular :term:`view name`, also known as a :term:`triad`. When a +request is generated and a :term:`router` performs its logic, it +locates these three values. These three values are fed to the +:term:`application registry` as a query to find "the best" view +callable. .. note:: To understand this process more deeply, it may be useful to read :ref:`router_chapter`. +A hybrid-mode application is one which performs :term:`traversal` +*after* a :term:`route` has already matched. + To "turn on" hybrid mode, use a :term:`route configuration` that includes a ``path`` argument that contains a special dynamic part: either ``*traverse`` or ``*subpath``. @@ -149,8 +155,8 @@ Using ``*traverse`` In a Route Path ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To create a hybrid application, combine traversal and URL dispatch by -using a ``<route>`` declaration that contains the special token -``*traverse`` in its path. +using route configuration that contains the special token +``*traverse`` in the route *path*. For example: .. code-block:: xml @@ -160,11 +166,11 @@ using a ``<route>`` declaration that contains the special token view=".views.home" /> -When the view attached to this route is invoked, :mod:`repoze.bfg` -will attempt to use :term:`traversal` against the context implied by -the :term:`root factory` of this route. The above example isn't very -useful unless you've defined a custom :term:`root factory` by passing -it to constructor of a :class:`repoze.bfg.configuration.Configurator` +When the this route is matched, :mod:`repoze.bfg` will attempt to use +:term:`traversal` against the context implied by the :term:`root +factory` of this route. The above example isn't very useful unless +you've defined a custom :term:`root factory` by passing it to +constructor of a :class:`repoze.bfg.configuration.Configurator` because the *default* root factory cannot be traversed (it has no useful ``__getitem__`` method). But let's imagine that your root factory looks like so: @@ -238,7 +244,8 @@ attribute which refers to the value of the ``<route>`` declaration's different view and (more importantly) a different :term:`view name`. It's :term:`view name` will be looked for during traversal. So if our URL is "http://example.com/one/two/a/another", the ``.views.another`` -view will be called. +view callable will be called instead of the *default* view callable +(the one implied by the route with the name ``home``). .. index:: pair: subpath; route @@ -335,14 +342,14 @@ application and you believe the "wrong" view is being called for a given request. A view is registered for a ``route`` either as its default view via -the ``view=`` attribute of a ``route`` declaration in ZCML *or* as a -standalone ``<view>`` declaration (or via the ``@bfg_route`` -decorator) which has a ``route_name`` that matches the route's name. -At startup time, when such a registration is encountered, the view is -registered for the ``context`` type ``None`` (meaning *any* context) -and a *special* request type which is dynamically generated. This -request type also derives from a "base" request type, which is what -allows it to match against views defined without a route name (see +the ``view=`` attribute of a ``route`` declaration in ZCML, via a +standalone ``<view>`` declaration, or via the ``@bfg_route`` decorator +which has a ``route_name`` that matches the route's name. At startup +time, when such a registration is encountered, the view is registered +for the ``context`` type ``None`` (meaning *any* context) and a +*special* request type which is dynamically generated. This request +type also derives from a "base" request type, which is what allows it +to match against views defined without a route name (see :ref:`globalviews_corner_case`). When a request URL matches a ``<route>`` path, the special request @@ -379,11 +386,11 @@ Yes, that was as painful for me to write as it was for you to read. Registering a Default View for a Route That has a ``view`` attribute ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -It is an error to provide *both* a ``view`` attribute on a ``<route>`` -declaration *and* a ``<view>`` declaration that serves as a "default -view" (a view with no ``name`` attribute or the empty ``name`` -attribute). For example, this pair of route/view statements will -generate a "conflict" error at startup time. +It is an error to provide *both* a ``view`` attribute on a ZCML +``<route>`` declaration *and* a ZCML ``<view>`` declaration that +serves as a "default view" (a view with no ``name`` attribute or the +empty ``name`` attribute). For example, this pair of route/view +statements will generate a "conflict" error at startup time. .. code-block:: xml @@ -453,7 +460,8 @@ Only the view associated with the route itself (``.views.abc``) will ever be invoked when the route matches, because the default view is always invoked when a route matches and when no post-match traversal is performed. To make the below ``<view>`` declaration non-useless, -you must the special ``*traverse`` token to the route's "path"., e.g.: +you must the special ``*traverse`` token to the route's "path." For +example: .. code-block:: xml |
