Browse files

Review.

  • Loading branch information...
1 parent 4100d9e commit 6fd5e123492efbe5f5f0cbb2ca0c0c4bf3fa0989 Chris McDonough committed Jan 17, 2010
Showing with 92 additions and 103 deletions.
  1. +2 −2 TODO.txt
  2. +26 −45 docs/narr/contextfinding.rst
  3. +64 −56 docs/narr/hybrid.rst
View
4 TODO.txt
@@ -4,10 +4,10 @@
- Review:
- [ ] Combining Traversal and URL Dispatch
-
[ ] Views
+ [ ] Static Resources
+
- Add a "route predicates" section where "Route Configuration"
(urldispatch.rst) is; also expand "Route Matching" section wrt
predicates.
View
71 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.
View
120 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

0 comments on commit 6fd5e12

Please sign in to comment.