diff options
| author | Chris McDonough <chrism@agendaless.com> | 2010-01-17 01:40:43 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2010-01-17 01:40:43 +0000 |
| commit | 223d4c07df32392075d22baab861588c2ad5c4f3 (patch) | |
| tree | 98b65e7cea6050ca91f3d715301984f93767ab21 /docs | |
| parent | c81116eecf7ecc07e25451cbe312c3794c905539 (diff) | |
| download | pyramid-223d4c07df32392075d22baab861588c2ad5c4f3.tar.gz pyramid-223d4c07df32392075d22baab861588c2ad5c4f3.tar.bz2 pyramid-223d4c07df32392075d22baab861588c2ad5c4f3.zip | |
More pass overhaul based on making contextfinding explicit within documentation.
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/authorintro.rst | 16 | ||||
| -rw-r--r-- | docs/index.rst | 2 | ||||
| -rw-r--r-- | docs/latexindex.rst | 2 | ||||
| -rw-r--r-- | docs/narr/contextfinding.rst (renamed from docs/narr/urlmapping.rst) | 67 | ||||
| -rw-r--r-- | docs/narr/firstapp.rst | 8 | ||||
| -rw-r--r-- | docs/narr/hooks.rst | 6 | ||||
| -rw-r--r-- | docs/narr/introduction.rst | 10 | ||||
| -rw-r--r-- | docs/narr/router.rst | 3 | ||||
| -rw-r--r-- | docs/narr/security.rst | 2 | ||||
| -rw-r--r-- | docs/narr/traversal.rst | 396 | ||||
| -rw-r--r-- | docs/narr/urldispatch.rst | 104 | ||||
| -rw-r--r-- | docs/narr/views.rst | 50 | ||||
| -rw-r--r-- | docs/tutorials/bfgwiki2/basiclayout.rst | 2 |
13 files changed, 359 insertions, 309 deletions
diff --git a/docs/authorintro.rst b/docs/authorintro.rst index c811818b8..f7496afd2 100644 --- a/docs/authorintro.rst +++ b/docs/authorintro.rst @@ -34,13 +34,15 @@ will be slightly harder, and it will take a little longer, but you'd be hard-pressed to find a better "first language." Web technology familiarity is assumed in various places within the -book. For example, the book describes various interactions in terms -of the HTTP protocol, but it does not describe how the HTTP protocol -works in detail. Like any good framework, though, :mod:`repoze.bfg` -shields you from needing to know the gory details of web protocols -such as HTTP, and you can usually skip such descriptions without -becoming "blocked," although you may lack some fundamental -understanding of how it works "under the hood" as a result. +book. For example, the book doesn't try to define common web-related +concepts like "URL" or "query string." Likewise, the book describes +various interactions in terms of the HTTP protocol, but it does not +describe how the HTTP protocol works in detail. Like any good web +framework, though, :mod:`repoze.bfg` shields you from needing to know +most of the gory details of web protocols and low-level data +structures. As a result, you can usually avoid becoming "blocked" +while you read this book even if you don't yet deeply understand web +technologies. .. index:: single: repoze.zope2 diff --git a/docs/index.rst b/docs/index.rst index dfdfbfe18..9535a2815 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -43,7 +43,7 @@ Narrative documentation in chapter form explaining how to use narr/firstapp narr/project narr/startup - narr/urlmapping + narr/contextfinding narr/views narr/static narr/webob diff --git a/docs/latexindex.rst b/docs/latexindex.rst index a350a5257..89563b779 100644 --- a/docs/latexindex.rst +++ b/docs/latexindex.rst @@ -30,7 +30,7 @@ Narrative Documentation narr/configuration narr/firstapp narr/project - narr/urlmapping + narr/contextfinding narr/views narr/static narr/webob diff --git a/docs/narr/urlmapping.rst b/docs/narr/contextfinding.rst index 7fdfef398..491113e62 100644 --- a/docs/narr/urlmapping.rst +++ b/docs/narr/contextfinding.rst @@ -1,38 +1,40 @@ .. index:: - triple: differences; URL dispatch; traversal - pair: mapping; URLs + pair: finding; context -.. _urlmapping_chapter: +.. _contextfinding_chapter: -Mapping URLs to Code --------------------- +Context Finding +--------------- -In order for a web application to perform any useful action, it needs -some way of finding and invoking code written by the application -developer based on parameters present in the :term:`request`. +In order for a web application to perform any useful action, the web +framework must provide a mechanism to find and invoke code written by +the application developer based on parameters present in the +:term:`request`. -:mod:`repoze.bfg` uses two separate but cooperating subsystems to -ultimately find and invoke code written by the application developer: -:term:`context finding` and :term:`view lookup` . +:mod:`repoze.bfg` uses two separate but cooperating subsystems to find +and invoke code written by the application developer: :term:`context +finding` and :term:`view lookup`. -- A :mod:`repoze.bfg` "context finding" subsystem is given a +- A :mod:`repoze.bfg` :term:`context finding` subsystem is given a :term:`request`; it is responsible for finding a :term:`context` object and a :term:`view name` based on information present in the request. -- The :mod:`repoze.bfg` view lookup subsystem is provided with a - :term:`request`, a :term:`context` and a :term:`view name`, and is - responsible for finding and invoking a :term:`view callable`. A - view callable is a specific bit of code that receives the +- Using the context and view name provided by :term:`context finding`, + the :mod:`repoze.bfg` view lookup subsystem is provided with a + :term:`request`, a :term:`context` and a :term:`view name`. It is + then responsible for finding and invoking a :term:`view callable`. + A view callable is a specific bit of code that receives the :term:`request` and which returns a :term:`response`, written and registered by the application developer. -These two subsystems are are used by :mod:`repoze.bfg` serially: a -:term:`context finding` subsystem does its job, then the result of -context finding is passed to the :term:`view lookup` subsystem. The -view lookup system finds a :term:`view callable` written by an -application developer, and invokes it. A view callable returns a -:term:`response`. The response is returned to the requesting user. +These two subsystems are are used by :mod:`repoze.bfg` serially: +first, a :term:`context finding` subsystem does its job. Then the +result of context finding is passed to the :term:`view lookup` +subsystem. The view lookup system finds a :term:`view callable` +written by an application developer, and invokes it. A view callable +returns a :term:`response`. The response is returned to the +requesting user. .. sidebar:: What Good is A Context Finding Subsystem? @@ -42,20 +44,21 @@ application developer, and invokes it. A view callable returns a into a single step. In these systems, a URL can map *directly* to a view callable. This makes them simpler to understand than systems which use distinct subsystems to locate a context and find - a view. However, explicitly using a context finding step provides - extra flexibility. For example, it makes it possible to protect - your application with declarative context-sensitive instance-level + a view. However, explicitly finding a context provides extra + flexibility. For example, it makes it possible to protect your + application with declarative context-sensitive instance-level :term:`authorization`, which is not well-supported in frameworks that do not provide a notion of a context. -There are two separate context finding subsystems in -:mod:`repoze.bfg`: :term:`traversal` and :term:`URL dispatch`. The -subsystems are documented within this chapter. They can be used -separately or they can be combined. +This chapter documents :term:`context finding`. There are two +separate :term:`context finding` subsystems in :mod:`repoze.bfg`: +:term:`traversal` and :term:`URL dispatch`. The subsystems are +documented within this chapter. They can be used separately or they +can be combined. -There is only one view lookup subsystem present in :mod:`repoze.bfg`. -It is not documented within this chapter. Instead, it is documented -within :ref:`views_chapter`. +There is only one :term:`view lookup` subsystem present in +:mod:`repoze.bfg`. Where appropriate, within this chapter, we +describe how view lookup interacts with context finding. .. toctree:: :maxdepth: 2 diff --git a/docs/narr/firstapp.rst b/docs/narr/firstapp.rst index c3adc936c..9bbe0d70a 100644 --- a/docs/narr/firstapp.rst +++ b/docs/narr/firstapp.rst @@ -10,10 +10,10 @@ explain in more detail how the application works. .. note:: If you're a "theory-first" kind of person, you might choose to read - :ref:`urlmapping_chapter` and :ref:`views_chapter` to augment your - understanding before diving into the code that follows, but it's - not necessary if -- like many programmers -- you're willing to "go - with the flow". + :ref:`contextfinding_chapter` and :ref:`views_chapter` to augment + your understanding before diving into the code that follows, but + it's not necessary if -- like many programmers -- you're willing to + "go with the flow". .. _helloworld_imperative: diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index 47495b68c..720fe8af9 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -163,9 +163,9 @@ Changing the Traverser ---------------------- The default :term:`traversal` algorithm that BFG uses is explained in -:ref:`how_bfg_traverses`. Though it is rarely necessary, this default -algorithm can be swapped out selectively for a different traversal -pattern via configuration. +:ref:`traversal_algorithm`. Though it is rarely necessary, this +default algorithm can be swapped out selectively for a different +traversal pattern via configuration. Use an ``adapter`` stanza in your application's ``configure.zcml`` to change the default traverser: diff --git a/docs/narr/introduction.rst b/docs/narr/introduction.rst index dfb841117..4e39a353e 100644 --- a/docs/narr/introduction.rst +++ b/docs/narr/introduction.rst @@ -14,8 +14,8 @@ application-specific. For example, the content of a web page served by one web application might be a representation of the contents of an accounting ledger, while the content of of a web page served by another might be a listing of songs. These applications probably -won't serve the same set of customers. However, both the -ledger-serving and song-serving applications can be written using +won't serve the same set of customers. However, both a ledger-serving +application and a song-serving application might be written using :mod:`repoze.bfg` because it is a very general open source Python web *framework*. As a framework, the primary job of :mod:`repoze.bfg` is to make it easier for a developer to create arbitrary web @@ -90,8 +90,8 @@ Openness <http://repoze.org/license.html>`_. This book usually refers to the framework by its full package name, -:mod:`repoze.bfg`. However, it is often referred to colloquially as -just "BFG" (the "repoze-dot" dropped) in conversation. +:mod:`repoze.bfg`. However, it is often referred to as just "BFG" +(the "repoze-dot" dropped) in conversation. .. index:: single: Repoze @@ -181,7 +181,7 @@ as :term:`Django`, :mod:`repoze.bfg` makes no assumptions about which persistence mechanisms you should use to build an application. Zope applications are typically reliant on :term:`ZODB`; :mod:`repoze.bfg` allows you to build :term:`ZODB` applications, but it has no reliance -on the ZODB package. Likewise, :term:`Django` tends to assume that +on the ZODB software. Likewise, :term:`Django` tends to assume that you want to store your application's data in a relational database. :mod:`repoze.bfg` makes no such assumption; it allows you to use a relational database but doesn't encourage or discourage the decision. diff --git a/docs/narr/router.rst b/docs/narr/router.rst index 05546f28e..c14118772 100644 --- a/docs/narr/router.rst +++ b/docs/narr/router.rst @@ -102,7 +102,6 @@ processing? This is a very high-level overview that leaves out various details. For more detail about subsystems invoked by the BFG router such as traversal, URL dispatch, views, and event processing, see -:ref:`urlmapping_chapter`, :ref:`traversal_chapter`, -:ref:`urldispatch_chapter`, :ref:`views_chapter`, and +:ref:`contextfinding_chapter`, :ref:`views_chapter`, and :ref:`events_chapter`. diff --git a/docs/narr/security.rst b/docs/narr/security.rst index ad91e9e29..f9e567b0f 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -15,7 +15,7 @@ Here's how it works at a high level: :term:`context finding`. A context is located differently depending on whether the application uses :term:`traversal` or :term:`URL dispatch`, but a context is ultimately found in either case. See - :ref:`urlmapping_chapter` for more information about context + :ref:`contextfinding_chapter` for more information about context finding. - A :term:`view callable` is located by :term:`view lookup` using the diff --git a/docs/narr/traversal.rst b/docs/narr/traversal.rst index 95df3d6b9..bb773c195 100644 --- a/docs/narr/traversal.rst +++ b/docs/narr/traversal.rst @@ -3,47 +3,136 @@ Traversal ========= -:term:`traversal` is a :term:`context finding` mechanism that is used -by :mod:`repoze.bfg`. :term:`traversal` is the act of finding a -:term:`context` and a :term:`view name` by walking over an *object -graph*, starting from a :term:`root` object, using a :term:`request` -object as a source of path information. +:term:`Traversal` is a :term:`context finding` mechanism. It is the +act of finding a :term:`context` and a :term:`view name` by walking +over an *object graph*, starting from a :term:`root` object, using a +:term:`request` object as a source of path information. In this chapter, we'll provide a high-level overview of traversal, we'll explain the concept of an *object graph*, and we'll show how traversal might be used within an application. .. index:: + single: traversal analogy + +A Traversal Analogy +------------------- + +We use an analogy to provide an introduction to :term:`traversal`. +Imagine an inexperienced UNIX computer user, wishing only to use the +command line to find a file and to invoke the ``cat`` command against +that file. Because he is inexperienced, the only commands he knows +how to use are ``cd``, which changes the current directory and +``cat``, which prints the contents of a file. And because he is +inexperienced, he doesn't understand that ``cat`` can take an absolute +path specification as an argument, so he doesn't know that you can +issue a single command command ``cat /an/absolute/path`` to get the +desired result. Instead, this user believes he must issue the ``cd`` +command, starting from the root, for each intermediate path segment, +*even the path segment that represents the file itself*. Once he gets +an error (because you cannot successfully ``cd`` into a file), he +knows he has reached the file he wants, and he will be able to execute +``cat`` against the resulting path segment. + +This inexperienced user's attempt to execute ``cat`` against the file +named ``/fiz/buz/myfile`` might be to issue the following set of UNIX +commands: + +.. code-block:: text + + cd / + cd fiz + cd buz + cd myfile + +The user now know he has found a *file*, because the ``cd`` command +issues an error when he executed ``cd myfile``. Now he knows that he +can run the ``cat`` command: + +.. code-block:: text + + cat myfile + +The contents of ``myfile`` are now printed on the user's behalf. + +:mod:`repoze.bfg` is very much like this inexperienced UNIX user as it +uses :term:`traversal` against an object graph. In this analogy, we +can map the ``cat`` program to the :mod:`repoze.bfg` concept of a +:term:`view callable`: it is a program that can be run against some +:term:`context` as the result of :term:`view lookup`. The file being +operated on in this analogy is the :term:`context` object; the context +is the "last node found" in a traversal. The directory structure is +the object graph being traversed. The act of progressively changing +directories to find the file as well as the handling of a ``cd`` error +as a stop condition is analogous to :term:`traversal`. + +The analogy we've used is not *exactly* correct, because, while the +naive user already knows which command he wants to invoke before he +starts "traversing" (``cat``), :mod:`repoze.bfg` needs to obtain that +information from the path being traversed itself. In +:term:`traversal`, the "command" meant to be invoked is a :term:`view +callable`. A view callable is derived via :term:`view lookup` from +the combination of the :term:`view name` and the :term:`context`. +Traversal is the act of obtaining these two items. + +.. index:: pair: traversal; high-level overview A High-Level Overview of Traversal ---------------------------------- :term:`Traversal` is dependent on information in a :term:`request` -object. The :term:`request` object contains URL path information in +object. Every :term:`request` object contains URL path information in the ``PATH_INFO`` portion of the :term:`WSGI` environment. The -``PATH_INFO`` portion of the WSGI environment is the URL data in a -request following the hostname and port number, but before any query -string elements or fragments, for example the ``/a/b/c`` portion of -the URL ``http://example.com/a/b/c?foo=1``. +``PATH_INFO`` portion of the WSGI environment is the portion of a +request's URL following the hostname and port number, but before any +query string elements or fragment element. For example the +``PATH_INFO`` portion of the the URL +``http://example.com:8080/a/b/c?foo=1`` is ``/a/b/c``. Traversal treats the ``PATH_INFO`` segment of a URL as a sequence of path segments. For example, the ``PATH_INFO`` string ``/a/b/c`` is -treated as the sequence ``['a', 'b', 'c']``. Traversal pops the first -element (``a``) from the path segment sequence and attempts to use it -as a lookup key into an object graph supplied by an application. If -that succeeeds, the :term:`context` temporarily becomes the object -found via that lookup. Then the next segment (``b``) is popped from -the sequence, and the object graph is queried for that segment; if -that lookup succeeds, the :term:`context` becomes that object. This -process continues until the path segment sequence is exhausted or any -lookup for a name in the sequence fails. In either case, a -:term:`context` is found. +coverted to the sequence ``['a', 'b', 'c']``. + +After the path info is converted, a lookup is performed against the +object graph for each path segment. Each lookup uses the +``__getitem__`` method of an object in the graph. + +For example, if the path info sequence is ``['a', 'b', 'c']``: + +- :term:`Traversal` pops the first element (``a``) from the path + segment sequence and attempts to call the root object's + ``__getitem__`` method using that value (``a``) as an argument; + we'll presume it succeeds. + +- When the root object's ``__getitem__`` succeeeds it will return an + object, which we'll call "A". The :term:`context` temporarily + becomes the "A" object. + +- The next segment (``b``) is popped from the path sequence, and the + "A" object's ``__getitem__`` is called with that value (``b``) as an + argument; we'll presume it succeeds. + +- When the "A" object's ``__getitem__`` succeeeds it will return an + object, which we'll call "B". The :term:`context` temporarily + becomes the "B" object. + +This process continues until the path segment sequence is exhausted or +a lookup for a path element fails. In either case, a :term:`context` +is found. + +Traversal "stops" when it 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`. If at any point during traversal any node in the +graph doesn't have a ``__getitem__`` method, or if the ``__getitem__`` +method of a node raises a :exc:`KeyError`, traversal ends immediately, +and that node becomes the :term:`context`. The results of a :term:`traversal` also include a :term:`view name`. The :term:`view name` is the *first* URL path segment in the set of ``PATH_INFO`` segments "left over" in the path segment list popped by -the traversal process. +the traversal process *after* traversal finds a context object. The combination of the :term:`context` object and the :term:`view name` found via traversal is used later in the same request by a @@ -62,11 +151,13 @@ The Object Graph When your application uses :term:`traversal` to resolve URLs to code, your application must supply an *object graph* to :mod:`repoze.bfg`. +This graph is represented by a :term:`root` object. -At system startup time, the :mod:`repoze.bfg` :term:`Router` is -configured with a callback known as a :term:`root factory`, supplied -by the application developer as the ``root_factory`` argument to a -:term:`Configurator`. +In order to supply a root object for an application, at system startup +time, the :mod:`repoze.bfg` :term:`Router` is configured with a +callback known as a :term:`root factory`. The root factory is +supplied by the application developer as the ``root_factory`` argument +to the application's :term:`Configurator`. Here's an example of a simple root factory: @@ -86,18 +177,19 @@ named ``config``: config = Configurator(root_factory=Root) -Making a declaration like this at startup means that your -:mod:`repoze.bfg` application will call the root factory (in this -case, the class ``Root``) to generate a root object whenever a request -enters the application. Usually a root factory for a traversal-based -application will be more complicated than the above ``Root`` object; -in particular it may be associated with a database connection or -another persistence mechanism. +Using the ``root_factory`` argument to a +:class:`repoze.bfg.configuration.Configurator` constructor tells your +:mod:`repoze.bfg` application to call this root factory to generate a +root object whenever a request enters the application. A root factory is passed a :term:`request` object and it is expected to return an object which represents the root of the object graph. -All :term:`traversal` will begin at this root object. The root object -is often an instance of a class which has a ``__getitem__`` method. +All :term:`traversal` will begin at this root object. Usually a root +factory for a traversal-based application will be more complicated +than the above ``Root`` object; in particular it may be associated +with a database connection or another persistence mechanism. A root +object is often an instance of a class which has a ``__getitem__`` +method. .. warning:: In :mod:`repoze.bfg` 1.0 and prior versions, the root factory was passed a term WSGI *environment* object (a dictionary) @@ -107,9 +199,10 @@ is often an instance of a class which has a ``__getitem__`` method. emulates the WSGI environment, so code expecting the argument to be a dictionary will continue to work. -If a :term:`root factory` is passed to the :mod:`repoze.bfg` -:term:`Configurator` constructor as the value ``None``, a *default* -root factory is used. +If no :term:`root factory` is passed to the :mod:`repoze.bfg` +:term:`Configurator` constructor, or the ``root_factory`` is specified +as the value ``None``, a *default* root factory is used. The default +root factory always returns an object that has no child nodes. .. sidebar:: Emulating the Default Root Factory @@ -138,96 +231,6 @@ concept of :term:`model` objects used by many other frameworks (and :mod:`repoze.bfg` APIs often refers to them as "models", as well). They are typically instances of Python classes. -.. index:: - single: traversal; algorithm - -.. _traversal_behavior: - -The :mod:`repoze.bfg` Traversal Algorithm ------------------------------------------ - -This section will attempt to explain the :mod:`repoze.bfg` traversal -algorithm. We'll provide an an analogy, a diagram of how the -traversal algorithm works, and some example traversal scenarios that -might aid in understanding how the traversal algorithm operates -against a specific object graph. - -The :ref:`views_chapter` chapter discusses :term:`view lookup` in -detail, and it is the canonical source for information about views. -Technically, :term:`traversal` is a :mod:`repoze.bfg` subsystem that -is separated from traversal entirely. However, we'll describe the -fundamental behavior of view lookup in the examples in the next few -sections to give you an idea of how traversal and view lookup -cooperate, because they are always used cooperatively. - -.. index:: - single: traversal analogy - -An Analogy -~~~~~~~~~~ - -We need to use an analogy to clarify how :mod:`repoze.bfg` traversal -works against an arbitrary object graph. - -Let's imagine an inexperienced UNIX computer user, wishing only to use -the command line to find a file and to invoke the ``cat`` command -against that file. Because he is inexperienced, the only commands he -knows how to use are ``cd``, which changes the current directory and -``cat``, which prints the contents of a file. And because he is -inexperienced, he doesn't understand that ``cat`` can take an absolute -path specification as an argument, so he doesn't know that you can -issue a single command command ``cat /an/absolute/path`` to get the -desired result. Instead, this user believes he must issue the ``cd`` -command, starting from the root, for each intermediate path segment, -*even the path segment that represents the file itself*. Once he gets -an error (because you cannot successfully ``cd`` into a file), he knows -he has reached the file he wants, and he will be able to execute -``cat`` against the resulting path segment. - -This inexperienced user's attempt to execute ``cat`` against the file -named ``/fiz/buz/myfile`` might be to issue the following set of UNIX -commands: - -.. code-block:: text - - cd / - cd fiz - cd buz - cd myfile - -The user now know he has found a *file*, because the ``cd`` command -issues an error when he executed ``cd myfile``. Now he knows that he -can run the ``cat`` command: - -.. code-block:: text - - cat myfile - -The contents of ``myfile`` are now printed on the user's behalf. - -:mod:`repoze.bfg` is very much like this inexperienced UNIX user as it -uses :term:`traversal` against an object graph. In this analogy, we -can map the ``cat`` program to the :mod:`repoze.bfg` concept of a -:term:`view callable`: it is a program that can be run against some -:term:`context` as the result of :term:`view lookup`. The file being -operated on in this analogy is the :term:`context` object; the context -is the "last node found" in a traversal. The directory structure is -the object graph being traversed. The act of progressively changing -directories to find the file as well as the handling of a ``cd`` error -as a stop condition is analogous to :term:`traversal`. - -The object graph is traversed, beginning at a root object, represented -by the root URL (``/``); if there are further path segments in the -path info of the request being processed, the root object's -``__getitem__`` is called with the next path segment, and it is -expected to return another graph object. The resulting object's -``__getitem__`` is called with the very next path segment, and it is -expected to return another graph object. This happens *ad infinitum* -until all path segments are exhausted. If at any point during -traversal any node in the graph doesn't *have* a ``__getitem__`` -method, or if the ``__getitem__`` of a node raises a :exc:`KeyError`, -traversal ends immediately, and the node becomes the :term:`context`. - The object graph consists of *container* nodes and *leaf* nodes. There is only one difference between a *container* node and a *leaf* node: *container* nodes possess a ``__getitem__`` method while *leaf* @@ -249,23 +252,47 @@ 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 object 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`. +In general, the object graph is traversed beginning at its root object +using a sequence of path elements described by the ``PATH_INFO`` of +the current request; if there are path segments, the root object's +``__getitem__`` is called with the next path segment, and it is +expected to return another graph object. The resulting object's +``__getitem__`` is called with the very next path segment, and it is +expected to return another graph object. This happens *ad infinitum* +until all path segments are exhausted. + +.. index:: + single: traversal algorithm + +.. _traversal_algorithm: + +The Traversal Algorithm +----------------------- + +This section will attempt to explain the :mod:`repoze.bfg` traversal +algorithm. We'll provide a description of the algorithm, a diagram of +how the algorithm works, and some example traversal scenarios that +might help you understand how the algorithm operates against a +specific object graph. + +We'll also talk a bit about :term:`view lookup` . The +:ref:`views_chapter` chapter discusses :term:`view lookup` in detail, +and it is the canonical source for information about views. +Technically, :term:`traversal` is a :mod:`repoze.bfg` subsystem that +is separated from traversal entirely. However, we'll describe the +fundamental behavior of view lookup in the examples in the next few +sections to give you an idea of how traversal and view lookup +cooperate, because they are almost always used together. .. index:: - pair: traversal; unicode pair: traversal; algorithm -.. _how_bfg_traverses: - -The Algorithm -~~~~~~~~~~~~~ +A Descrption of The Traversal Algorithm +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When a user requests a page from your :mod:`traversal` -powered -application, the system uses this algorithm to determine which Python -code to execute: +application, the system uses this algorithm to find a :term:`context` +and a :term:`view name`. #. The request for the page is presented to the :mod:`repoze.bfg` :term:`router` in terms of a standard :term:`WSGI` request, which @@ -281,12 +308,22 @@ code to execute: #. The router uses the WSGI environment's ``PATH_INFO`` information to determine the path segments to traverse. The leading slash is stripped off ``PATH_INFO``, and the remaining path segments are - split on the slash character to form a traversal sequence, so a - request with a ``PATH_INFO`` variable of ``/a/b/c`` maps to the - traversal sequence ``[u'a', u'b', u'c']``. Note that each of the - path segments in the sequence is converted to Unicode using the - UTF-8 decoding; if the decoding fails, a :exc:`TypeError` is - raised. + split on the slash character to form a traversal sequence. + + The traversal algorithm by default attempts to first URL-unquote + and then Unicode-decode each path segment derived from + ``PATH_INFO`` from its natural byte string (``str`` type) + representation. URL unquoting is performed using the Python + standard library ``urllib.unquote`` function. Conversion from a + URL-decoded string into Unicode is attempted using the UTF-8 + encoding. If any URL-unquoted path segment in ``PATH_INFO`` is + not decodeable using the UTF-8 decoding, a :exc:`TypeError` is + raised. A segment will be fully URL-unquoted and UTF8-decoded + before it is passed it to the ``__getitem__`` of any model object + during traversal. + + Thus, a request with a ``PATH_INFO`` variable of ``/a/b/c`` maps + to the traversal sequence ``[u'a', u'b', u'c']``. #. :term:`Traversal` begins at the root object returned by the root factory. For the traversal sequence ``[u'a', u'b', u'c']``, the @@ -311,21 +348,13 @@ code to execute: :term:`context`. If the path has been exhausted when traversal ends, the :term:`view name` is deemed to be the empty string (``''``). However, if the path was *not* exhausted before - traversal terminated, the first remaining path element is treated + traversal terminated, the first remaining path segment is treated as the view name. - Any subsequent path elements after the view name are deemed the - :term:`subpath`. The subpath is always a sequence of path - segments that come from ``PATH_INFO`` that are "left over" after - traversal has completed. For instance, if ``PATH_INFO`` was - ``/a/b`` and the root returned an "object ``a``", and "object - ``a``" subsequently returned an "object ``b``", the router deems - that the context is "object ``b``", the view name is the empty - string, and the subpath is the empty sequence. On the other hand, - if ``PATH_INFO`` was ``/a/b/c`` and "object ``a``" was found but - raised a ``KeyError`` for the name ``b``, the router deems that - the context is "object ``a``", the view name is ``b`` and the - subpath is ``('c',)``. +#. Any subsequent path elements after the :term:`view name` is found + are deemed the :term:`subpath`. The subpath is always a sequence + of path segments that come from ``PATH_INFO`` that are "left over" + after traversal has completed. Once :term:`context` and :term:`view name` and associated attributes such as the :term:`subpath` are located, the job of :term:`traversal` @@ -334,18 +363,7 @@ caller, the :mod:`repoze.bfg` :term:`Router`, which subsequently invokes :term:`view lookup` with the context and view name information. -Note well that the traversal machinery by default attempts to first -URL-unquote and then Unicode-decode each path element in ``PATH_INFO`` -from its natural byte string (``str`` type) representation. URL -unquoting is performed using the Python standard library -``urllib.unquote`` function. Conversion from a URL-decoded string -into Unicode is attempted using the UTF-8 encoding. If any -URL-unquoted path segment in ``PATH_INFO`` is not decodeable using the -UTF-8 decoding, a :exc:`TypeError` is raised. A segment will be fully -URL-unquoted and UTF8-decoded before it is passed it to the -``__getitem__`` of any model object during traversal. - -The standard traversal algorithm exposes two special cases: +The traversal algorithm exposes two special cases: - You will often end up with a :term:`view name` that is the empty string as the result of a particular traversal. This indicates that @@ -360,23 +378,28 @@ The standard traversal algorithm exposes two special cases: views that may have the same names as model instance names in the graph unambiguously. +Finally, traversal is responsible for locating a :term:`virtual root`. +A virtual root is used during "virtual hosting"; see the +:ref:`vhosting_chapter` chapter for information. We won't speak more +about it in this chapter. + .. image:: modelgraphtraverser.png .. index:: - pair: traversal; example + pair: traversal; examples -Traversal Examples -~~~~~~~~~~~~~~~~~~ +Traversal Algorithm Examples +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ No one can be expected to understand the traversal algorithm by analogy and description alone, so let's examine some traversal scenarios that use concrete URLs and object graph compositions. Let's pretend the user asks for -``http://example.com/foo/bar/baz/biz/buz.txt``. Let's pretend that the -request's ``PATH_INFO`` in that case is ``/foo/bar/baz/biz/buz.txt``. -Let's further pretend that when this request comes in that we're -traversing the following graph:: +``http://example.com/foo/bar/baz/biz/buz.txt``. The request's +``PATH_INFO`` in that case is ``/foo/bar/baz/biz/buz.txt``. Let's +further pretend that when this request comes in that we're traversing +the following object graph:: /-- | @@ -469,12 +492,11 @@ view lookup asks the :term:`application registry` this question: configuration` with the name ``buz.txt`` that can be used for class ``Biz``. -Let's say that question is answered by the application registry with -the equivalent of "here you go, here's a bit of code that is willing -to deal with that case"; the application registry returns a -:term:`view callable`. The view callable is then called with the -current :term:`WebOb` :term:`request` as the sole argument: -``request``; it is expected to return a response. +Let's say that question is answered by the application registry; in +such a situation, the application registry returns a :term:`view +callable`. The view callable is then called with the current +:term:`WebOb` :term:`request` as the sole argument: ``request``; it is +expected to return a response. .. sidebar:: The Example View Callables Accept Only a Request; How Do I Access the Context? @@ -495,8 +517,16 @@ current :term:`WebOb` :term:`request` as the sole argument: References ---------- -A tutorial showing how :term:`traversal` can be used to create a +A tutorial showing how :term:`traversal` can be used within a :mod:`repoze.bfg` application exists in :ref:`bfg_wiki_tutorial`. See the :ref:`views_chapter` chapter for detailed information about :term:`view lookup`. + +The :mod:`repoze.bfg.traversal` module contains API functions that +deal with traversal, such as traversal invocation from within +application code. + +The :func:`repoze.bfg.url.model_url` function generates a URL when +given an object retrived from an object graph. + diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index 604eb6814..8a7b86f2d 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -11,9 +11,11 @@ augment or replace :term:`traversal` as a :term:`context finding` mechanism, allowing URL pattern matching to have the "first crack" at resolving a given URL to :term:`context` and :term:`view name`. -Using URL dispatch exclusively allows you to avoid thinking about your -application in terms of "contexts" and "view names" entirely. Many -applications don't need :mod:`repoze.bfg` features -- such as +Although it is a "context-finding" mechanism, ironically, using URL +dispatch exclusively allows you to avoid thinking about your +application in terms of "contexts" and "view names" entirely. + +Many applications don't need :mod:`repoze.bfg` features -- such as declarative security via an :term:`authorization policy` -- that benefit from having any visible separation between :term:`context finding` and :term:`view lookup`. To this end, URL dispatch provides @@ -51,7 +53,7 @@ provided to :mod:`repoze.bfg`, a route-specific :term:`context` and :term:`view name` will be generated. In this circumstance, :mod:`repoze.bfg` will shortcut :term:`traversal`, and will invoke :term:`view lookup` using the context and view name generated by URL -dispatch. If the route named a :term:`view callable` in its +dispatch. If the matched route names a :term:`view callable` in its configuration, that view callable will be invoked when view lookup is performed. @@ -97,8 +99,11 @@ registry`. Here's an example: Configuring a Route via ZCML ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Instead of using the imperative method of adding a route, you can use -:term:`ZCML` for the same purpose. For example: +Instead of using the imperative +:meth:`repoze.bfg.configuration.Configurator.add_route` method to add +a new route, you can alternately use :term:`ZCML`. For example, the +following :term:`ZCML declaration` causes a route to be added to the +application. .. code-block:: xml :linenos: @@ -153,7 +158,7 @@ invoked when the associated route path pattern matches during a request. The purpose of making it possible to specify a view callable within a -route configuration is to avoid the need for developers to deeply +route configuration is to prevent developers from needing to deeply understand the details of :term:`context finding` and :term:`view lookup`. When a route names a view callable, and a request enters the system which matches the path of the route, the result is simple: the @@ -190,12 +195,13 @@ matched during a request. To do so: callable registered using that interface by the route configuration to service requests that match the route path pattern. -In this way, we supply a system that still consumes the :term:`context -finding` and :term:`view lookup` services provided by -:mod:`repoze.bfg`, but which does not require that a developer -understand either of them if he doesn't want to. It also means that -we can allow a developer to combine :term:`URL dispatch` and -:term:`traversal` in exceptional cases (see :ref:`hybrid_chapter`). +In this way, we supply a shortcut to the developer. Under the hood, +:mod:`repoze.bfg` still consumes the :term:`context finding` and +:term:`view lookup` subsystems provided by :mod:`repoze.bfg`, but in a +way which does not require that a developer understand either of them +if he doesn't want or need to. It also means that we can allow a +developer to combine :term:`URL dispatch` and :term:`traversal` in +various exceptional cases as documented in :ref:`hybrid_chapter`. .. index:: pair: URL dispatch; path pattern syntax @@ -280,15 +286,15 @@ not need to be preceded by a slash. For example: .. code-block:: text - foo/:baz/:bar*traverse + foo/:baz/:bar*fizzle The above pattern will match these URLs, generating the following matchdicts: .. code-block:: text - foo/1/2/ -> {'baz':1, 'bar':2, 'traverse':()} - foo/abc/def/a/b/c -> {'baz':abc, 'bar':def, 'traverse':('a', 'b', 'c')} + foo/1/2/ -> {'baz':1, 'bar':2, 'fizzle':()} + foo/abc/def/a/b/c -> {'baz':abc, 'bar':def, 'fizzle':('a', 'b', 'c')} Note that when a ``*stararg`` remainder match is matched, the value put into the matchdict is turned into a tuple of path segments @@ -298,7 +304,7 @@ the following pattern: .. code-block:: text - foo/*traverse + foo/*fizzle When matching the following path: @@ -310,7 +316,7 @@ Will generate the following matchdict: .. code-block:: text - {'traverse':(u'La Pe\xf1a', u'a', u'b', u'c')} + {'fizzle':(u'La Pe\xf1a', u'a', u'b', u'c')} .. index:: triple: ZCML directive; route; examples @@ -328,12 +334,12 @@ declaration ordering is very important. The order that routes declarations are evaluated is the order in which they are added to the application at startup time. This is unlike -:term:`traversal`, which depends on the emergent behavior which -happens as a result of traversing a graph. +:term:`traversal`, which depends on emergent behavior which happens as +a result of traversing a graph. -For routes configured via ZCML, the order that routes are evaluated is -the order in which they appear in the ZCML relative to each other. -For routes added via the +The order that route are evaluated when they are defined via +:term:`ZCML` is the order in which they appear in the ZCML relative to +each other. For routes added via the :mod:`repoze.bfg.configuration.Configurator.add_route` method, the order that routes are evaluated is the order in which they are added to the configuration imperatively. @@ -344,12 +350,13 @@ to the configuration imperatively. Route Factories ~~~~~~~~~~~~~~~ -A "route" configuration declaration can mention a "factory". When a -factory is attached to a route, the :term:`root factory` passed at -startup time to the :term:`Configurator` is ignored; instead the -factory associated with the route is used to generate a :term:`root` -object. This object will usually be used as the :term:`context` of -the view callable ultimately found via :term:`view lookup`. +A "route" configuration declaration can mention a "factory". When +that route matches a request, and a factory is attached to a route, +the :term:`root factory` passed at startup time to the +:term:`Configurator` is ignored; instead the factory associated with +the route is used to generate a :term:`root` object. This object will +usually be used as the :term:`context` of the view callable ultimately +found via :term:`view lookup`. .. code-block:: xml @@ -367,10 +374,10 @@ related to each particular route. Supplying a different context for each route is useful when you're trying to use a :mod:`repoze.bfg` :term:`authorization policy` to provide declarative "context-sensitive" security checks; each context -can maintain a separate :term:`ACL` (as in -:ref:`using_security_with_urldispatch`). It is also useful when you -wish to combine URL dispatch with :term:`traversal` (as in -:ref:`hybrid_chapter`). +can maintain a separate :term:`ACL`, as in +:ref:`using_security_with_urldispatch`. It is also useful when you +wish to combine URL dispatch with :term:`traversal` as documented +within :ref:`hybrid_chapter`. Route Matching -------------- @@ -391,7 +398,7 @@ name` are generated, and the context, the view name, and the resulting request are handed off to :term:`view lookup`. This process is otherwise known as :term:`context finding`. During view lookup, if any ``view`` argument was provided within the matched route -configuration, this view is called. +configuration, the :term:`view callable` it points to is called. If no route matches after all route patterns are exhausted, :mod:`repoze.bfg` falls back to :term:`traversal` to do :term:`context @@ -609,9 +616,9 @@ completely equivalent to this example provided in view="mypackage.views.site_view" /> -In fact the spelling in :ref:`urldispatch_example1` is just syntactic -sugar for the more verbose spelling where the route declaration and -the view declaration are spelled separately. +In fact, the spelling which names a ``view`` attribute is just +syntactic sugar for the more verbose spelling which contains separate +view and route registrations. More uses for this style of associating views with routes are explored in :ref:`hybrid_chapter`. @@ -780,6 +787,12 @@ Then in the ``configure.zcml`` of your package, inject the following: This will cause the DBSession to be removed whenever the WSGI environment is destroyed (usually at the end of every request). +Alternate mechanisms for performing this sort of cleanup exist; an +alternate mechanism which uses cleanup services offered by the +``repoze.tm2`` package is used in the SQLAlchemy-related ``paster`` +templates generated by :mod:`repoze.bfg` and within +:ref:`sql_tm2_cleanup` within the :ref:`bfg_sql_wiki_tutorial`. + .. index:: pair: URL dispatch; security @@ -821,8 +834,8 @@ not very ambitious. .. note:: See :ref:`security_chapter` for more information about :mod:`repoze.bfg` security and ACLs. -Context-and-Request View Callables ----------------------------------- +Using Context Within a View Callable +------------------------------------ When using :term:`url dispatch` exclusively in an application (as opposed to using both url dispatch *and* :term:`traversal` in the same @@ -832,9 +845,12 @@ your route definitions. However, if you do use a ``factory`` attribute on your route definitions, you may be very interested in the :term:`context` of the -view. :mod:`repoze.bfg` supports view callables defined with two -arguments: ``context`` and ``request``. For example, the below view -statement is completely equivalent to the above view statement: +view. You can access the ``context`` of a view within the body of a +view callable via ``request.context`` + +:mod:`repoze.bfg` also supports view callables defined with two +arguments: ``context`` and ``request``. For example, the below +function can be used as a view callable. .. code-block:: python :linenos: @@ -848,10 +864,6 @@ The ``context`` passed to this view will be an instance returned by the default root factory or an instance returned by the ``factory`` argument to your route definition. -Even if you use the request-only argument format in view callables, -you can still get to the ``context`` of the view (if necessary) by -accessing ``request.context``. - See :ref:`request_and_context_view_definitions` for more information. References diff --git a/docs/narr/views.rst b/docs/narr/views.rst index 4a329153c..dbfd44659 100644 --- a/docs/narr/views.rst +++ b/docs/narr/views.rst @@ -4,30 +4,32 @@ Views ===== The primary job of any :mod:`repoze.bfg` application is is to find and -call a :term:`view callable` when a :term:`request` reaches the -application. A :term:`view callable` is invoked when a request enters -your application: it "does something", then returns a response. All -view callables are written by you, the application developer. +invoke a :term:`view callable` when a :term:`request` reaches the +application. View callables are bits of code written by you -- the +application developer -- which do something interesting in response to +a request made to your application. -The :ref:`urlmapping_chapter` describes how a :term:`context` and a -:term:`view name` are computed using information from the -:term:`request` via the process of :term:`context finding`. But -neither the context nor the view name found very useful unless those -elements can eventually be mapped to a :term:`view callable`. - -.. note:: - - A :term:`view callable` is oten referred to in conversational - shorthand as a :term:`view`; in this documentation we need to be - more precise, however, due to the difference between view - *configuration* and the code that implements a view *callable*. +The :ref:`contextfinding_chapter` describes how a :term:`context` and +a :term:`view name` are computed using information from the +:term:`request`. But neither the context nor the view name found are +very useful unless those elements can eventually be mapped to a +:term:`view callable`. The job of actually locating and invoking the "best" :term:`view callable` is the job of the :term:`view lookup` subsystem. The view -lookup subsystem compares information found via -:term:`context finding` against :term:`view configuration` statements -made by the developer to choose "the best" view callable for a -specific circumstance. +lookup subsystem compares information supplied by :term:`context +finding` against :term:`view configuration` statements made by the +developer to choose the most appropriate view callable for a specific +request. + +.. note:: + + A :mod:`repoze.bfg` :term:`view callable` is oten referred to in + conversational shorthand as a :term:`view`. In this documentation, + however, we need to use less ambiguous terminology because there is + a significant difference between view *configuration*, the code + that implements a view *callable*, and the process of view + *lookup*. Provided within this chapter is documentation of the process of creating view callables, documentation about performing view @@ -870,8 +872,8 @@ rendered in a request that has a ``;charset=utf-8`` stanza on its .. _view_configuration: -View Configuration ------------------- +View Configuration: Mapping a Context to a View +----------------------------------------------- A developer makes a :term:`view callable` available for use within a :mod:`repoze.bfg` application via :term:`view configuration`. A view @@ -1482,8 +1484,8 @@ user does not possess the ``add`` permission relative to the current .. _view_lookup: -View Lookup ------------ +View Lookup and Invocation +-------------------------- :term:`View lookup` is the :mod:`repoze.bfg` subsystem responsible for finding an invoking a :term:`view callable`. The view lookup diff --git a/docs/tutorials/bfgwiki2/basiclayout.rst b/docs/tutorials/bfgwiki2/basiclayout.rst index d30d919c1..70e7a8a62 100644 --- a/docs/tutorials/bfgwiki2/basiclayout.rst +++ b/docs/tutorials/bfgwiki2/basiclayout.rst @@ -105,6 +105,8 @@ Here is the source for ``models.py``: object. It also calls the ``populate`` function, to do initial database population. +.. _sql_tm2_cleanup: + App Startup with ``run.py`` --------------------------- |
