diff options
| author | Chris McDonough <chrism@agendaless.com> | 2008-07-28 05:31:47 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2008-07-28 05:31:47 +0000 |
| commit | 178623bbd8e9aab75b6206ef69f67b62edb3d12e (patch) | |
| tree | 2442c1d41bdb13a41e4ab841fddcc091d5804ca2 /docs/narr | |
| parent | 96e65d8e6d47a1b806c4d281e6890f77f86407c3 (diff) | |
| download | pyramid-178623bbd8e9aab75b6206ef69f67b62edb3d12e.tar.gz pyramid-178623bbd8e9aab75b6206ef69f67b62edb3d12e.tar.bz2 pyramid-178623bbd8e9aab75b6206ef69f67b62edb3d12e.zip | |
Tweaks.
Diffstat (limited to 'docs/narr')
| -rw-r--r-- | docs/narr/introduction.rst | 10 | ||||
| -rw-r--r-- | docs/narr/models.rst | 29 | ||||
| -rw-r--r-- | docs/narr/project.rst | 200 | ||||
| -rw-r--r-- | docs/narr/security.rst | 32 | ||||
| -rw-r--r-- | docs/narr/traversal.rst | 181 |
5 files changed, 248 insertions, 204 deletions
diff --git a/docs/narr/introduction.rst b/docs/narr/introduction.rst index 61b03b3dc..bf2f07e19 100644 --- a/docs/narr/introduction.rst +++ b/docs/narr/introduction.rst @@ -16,11 +16,15 @@ Similarities with Other Frameworks :mod:`repoze.bfg` was inspired by Zope, Django, and Pylons. -:mod:`repoze.bfg`'s traversal is inspired by Zope. :mod:`repoze.bfg` +:mod:`repoze.bfg` traversal is inspired by Zope. :mod:`repoze.bfg` uses the Zope Component Architecture ("CA") internally, as do Zope 2, Zope 3, and Grok. Developers don't interact with the CA very much during typical development, however; it's mostly used by the framework -developer rather than the application developer. +developer rather than the application developer. :mod:`repoze.bfg` +developers use :term:`ZCML` (an XML dialect) to perform various +configuration tasks; in particular, as in Zope3, one more more +:term:`view` functions is associated with a :term:`model` type via +ZCML. Like Pylons, :mod:`repoze.bfg` is mostly policy-free. It makes no assertions about which database you should use, and its built-in @@ -62,7 +66,7 @@ happens to be true for :mod:`repoze.bfg`:: framework - that is, "model", "template", and "view." That breakdown makes much more sense. -:mod:`repoze.bfg` 's skeleton code generator generates a directory +The skeleton code generator of :mod:`repoze.bfg` generates a directory layout very simliar to the directory layout suggested by the `Django Book <http://www.djangobook.com/>`_ . Additionally, as suggested above, the concepts of :term:`view`, :term:`model` and diff --git a/docs/narr/models.rst b/docs/narr/models.rst index cff04ea18..1e5685ed6 100644 --- a/docs/narr/models.rst +++ b/docs/narr/models.rst @@ -1,9 +1,9 @@ Models ====== -*Models* are typically simple Python classes defined in a module. - Model *instances* make up the graph that :mod:`repoze.bfg` is willing - to traverse. +A :term:`model` is typically a simple Python class defined in a +module. Model *instances* make up the graph that :mod:`repoze.bfg` is +willing to traverse. Defining a Model ---------------- @@ -26,11 +26,11 @@ An example of a model describing a blog entry:: self.created = datetime.datetime.now() A model consists of two things: the object which defines the model -(above as the class ``IBlogEntry``), and an *interface* attached to -the model object. An interface simply tags the model object with a -"type" that can be referred to within view configuration. A model -object can implement zero or more interfaces. The interface must be -an instance of a class that inherits from +(above as the class ``IBlogEntry``), and an :term:`interface` attached +to the model object. An interface simply tags the model object with a +"type" that can be referred to within the :term:`application +registry`. A model object can implement zero or more interfaces. The +interface must be an instance of a class that inherits from ``zope.interface.Interface``. You specify that a model *implements* an interface by using the @@ -67,13 +67,14 @@ Location-Aware Model Instances by via ``__getitem__``. If you choose not to manage the ``__name__`` and ``__parent__`` - attributes of your models "by hand", :mod:`repoze.bfg`` is willing - to help you do this. If your "root" node claims it implements the + attributes of your models "by hand", :mod:`repoze.bfg`` is willing to + help you do this. If your "root" node claims it implements the interface ``zope.location.interfaces.ILocation``, you don't need to - manage these attributes by hand. During traversal, if the root node - says it implements ``ILocation``, bfg will wrap each child in a - LocationProxy which will dynamically assign a ``__name__`` and a - ``__parent__`` to it, recursively. + manage these attributes by hand. During :term:`traversal`, if the + root node says it implements the ``ILocation`` :term:`interface`, + :mod:`repoze.bfg` will wrap each child in a ``LocationProxy`` which + will dynamically assign a ``__name__`` and a ``__parent__`` to it, + recursively. If you choose to make use of the location-based dynamic assignment of ``__parent__`` and ``__name__``, the root node must have a diff --git a/docs/narr/project.rst b/docs/narr/project.rst index f67e28e1e..53ba15b74 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -10,8 +10,8 @@ creation of a new project by answering a series of questions. Creating the Project -------------------- -To start a :mod:`repoze.bfg` project, use the ``paster create`` -facility:: +To start a :mod:`repoze.bfg` :term:`project`, use the ``paster +create`` facility:: $ paster create -t bfg @@ -49,27 +49,26 @@ project we name ``myproject``:: Running /Users/chrism/projects/repoze-devel/bfg/bin/python setup.py egg_info As a result of the above, a project is created in a directory named -``myproject``. That directory is a :term:`setuptools` *project* -directory from which a Python setuptools *distribution* can be +``myproject``. That directory is a :term:`setuptools` :term:`project` +directory from which a Python setuptools :term:`distribution` can be created. The ``setup.py`` file in that directory can be used to distribute your application, or install your application for -deployment or development. A sample ``PasteDeploy`` ``.ini`` file +deployment or development. A sample :term:`PasteDeploy` ``.ini`` file named ``myproject.ini`` will also be created in the project directory. -You can use this to run your application. +You will use the ``paster serve`` command against this ``ini`` file to +run your application. -The main ``myproject`` contains an additional subdirectory (also named -``myproject``) representing a Python pakckage which holds very simple -bfg sample code. This is where you'll edit your application's Python -code and templates. +The main ``myproject`` directory contains an additional subdirectory +(also named ``myproject``) representing a Python :term:`package` which +holds very simple :mod:`repoze.bfg` sample code. This is where you'll +edit your application's Python code and templates. Installing your Newly Created Project for Development ----------------------------------------------------- -Using your favorite Python interpreter (using a `virtualenv -<http://pypi.python.org/pypi/virtualenv>`_ is suggested in order to -isolate your application from your system Python's packages), invoke -the following command when inside the project directory against the -generated ``setup.py``:: +Using your favorite Python interpreter (or, better, the interpreter +from a :term:`virtualenv`), invoke the following command when inside +the project directory against the generated ``setup.py``:: $ python setup.py develop @@ -79,8 +78,9 @@ Elided output from a run of this command is shown below:: ... Finished processing dependencies for myproject==0.1 -This will install your application 's package into the interpreter so -it can be found and run as a WSGI application inside a WSGI server. +This will install your application 's :term:`package` into the +interpreter so it can be found and run as a :term:`WSGI` application +inside a WSGI server. Running The Tests For Your Application -------------------------------------- @@ -139,20 +139,21 @@ port 5432. Viewing the Application ----------------------- -Visit http://localhost:5432/ in your browser. You will see:: +Visit *http://localhost:5432/* in your browser. You will see:: Welcome to myproject -That's the page shown by default when you visit a generated -application. +That's the page shown by default when you visit an unmodified ``paster +create``-generated application. The Project Structure --------------------- -Our generated :mod:`repoze.bfg` application is a setuptools *project* -(named ``myproject``), which contains a Python package (which is -*also* named ``myproject``; the paster template generates a project -which contains a package that shares its name). +Our generated :mod:`repoze.bfg` application is a setuptools +:term:`project` (named ``myproject``), which contains a Python +:term:`package` (which is *also* named ``myproject``; the paster +template generates a project which contains a package that shares its +name). The ``myproject`` project has the following directory structure:: @@ -172,64 +173,71 @@ The ``myproject`` project has the following directory structure:: |-- myproject.ini `-- setup.py -The ``myproject`` *Project* ---------------------------- +The ``myproject`` :term:`Project` +--------------------------------- -The ``myproject`` project is the distribution and deployment wrapper -for your application. It contains both the ``myproject`` *package* -representing your application as well as files used to describe, run, -and test your application. +The ``myproject`` :term:`project` is the distribution and deployment +wrapper for your application. It contains both the ``myproject`` +:term:`package` representing your application as well as files used to +describe, run, and test your application. -#. ``CHANGES.txt`` describes the changes you've made to the application. +#. ``CHANGES.txt`` describes the changes you've made to the + application. It is conventionally written in + :term:`ReStructuredText` format. -#. ``README.txt`` describes the application in general. +#. ``README.txt`` describes the application in general. It is + conventionally written in :term:`RestructuredText` format. #. ``ez_setup.py`` is a file that is used by ``setup.py`` to install - setuptools if the executing user does not have it installed. + :term:`setuptools` if the executing user does not have it + installed. -#. ``myproject.ini`` is a PasteDeploy configuration file that can be - used to execute your application. +#. ``myproject.ini`` is a :term:`PasteDeploy` configuration file that + can be used to execute your application. #. ``setup.py`` is the file you'll use to test and distribute your - application. It is a standard distutils/setuptools ``setup.py`` file. + application. It is a standard :term:`setuptools` ``setup.py`` + file. -It also contains the ``myproject`` *package*, described below. +It also contains the ``myproject`` :term:`package`, described below. -The ``myproject`` *Package* ---------------------------- +The ``myproject`` :term:`Package` +--------------------------------- -The ``myproject`` package lives inside the ``myproject`` project. It -contains: +The ``myproject`` :term:`package` lives inside the ``myproject`` +:term:`project`. It contains: #. An ``__init__.py`` file which signifies that this is a Python - package. It is conventionally empty, save for a single comment at - the top. + :term:`package`. It is conventionally empty, save for a single + comment at the top. -#. A ``configure.zcml`` file which maps view names to model types. - This is also known as the "application registry", although it - also often contains non-view-related declarations. +#. A ``configure.zcml`` is a :term:`ZCML` file which maps view names + to model types. This is also known as the :term:`application + registry`. -#. A ``models.py`` module, which contains model code. +#. A ``models.py`` module, which contains :term:`model` code. #. A ``run.py`` module, which contains code that helps users run the application. -#. A ``templates`` directory, which is full of zc3.pt and/or XSL - templates. +#. A ``templates`` directory, which is full of :term:`z3c.pt` and/or + :term:`XSLT` templates. -#. A ``tests.py`` module, which contains test code. +#. A ``tests.py`` module, which contains unit test code for the + application. -#. A ``views.py`` module, which contains view code. +#. A ``views.py`` module, which contains view code for the + application. -These are purely conventions established by the Paster template: +These are purely conventions established by the ``paster`` template: :mod:`repoze.bfg` doesn't insist that you name things in any particular way. ``configure.zcml`` ~~~~~~~~~~~~~~~~~~ -The ``configure.zcml`` (representing the application registry) looks -like so: +The ``configure.zcml`` represents the :term:`application +registry`. It looks like so: .. literalinclude:: myproject/myproject/configure.zcml :linenos: @@ -244,7 +252,17 @@ like so: #. Lines 8-11 register a single view. It is ``for`` model objects that support the IMyModel interface. The ``view`` attribute points - at a Python function that does all the work for this view. + at a Python function that does all the work for this view. Note + that the values of both the ``for`` attribute and the ``view`` + attribute begin with a single period. Names that begin with a + period are "shortcuts" which point at files relative to the + :term:`package` in which the ``configure.zcml`` file lives. In + this case, since the ``configure.zcml`` file lives within the + ``myproject`` project, the shorcut ``.models.IMyModel`` could also + be spelled ``myproject.models.IMyModel`` (forming a full Python + dotted-path name to the ``IMyModel`` class). Likewise the shortcut + ``.views.my_view`` could be replaced with + ``myproject.views.my_view``. ``views.py`` ~~~~~~~~~~~~ @@ -257,31 +275,36 @@ in the model, and the HTML given back to the browser. :linenos: #. Lines 3-5 provide the ``my_view`` that was registered as the view. - ``configure.zcml`` said that the default URL for IMyModel content - should run this ``my_view`` function. + ``configure.zcml`` said that the default URL for ``IMyModel`` + content should run this ``my_view`` function. - The function is handed two pieces of information: the ``context`` - and the ``request``. The ``context`` is the data at the current - hop in the URL. (That data comes from the model.) The request is - an instance of a WebOb request. + The function is handed two pieces of information: the + :term:`context` and the term:`request`. The *context* is the term + :term:`model` found via :term:`traversal` (or via :term:`URL + dispatch`). The *request* is an instance of the :term:`WebOb` + ``Request`` class representing the browser's request to our server. -#. The model renders a template and returns the result as the - response. +#. The view renders a :term:`template` and returns the result as the + :term:`response`. .. note:: - This example uses ``render_template_to_response`` which allows the - view author to think only in terms of templates. If you want more - control over the response, use ``render_template`` and create your - own ``WebOb`` Response object to return. + This example uses ``render_template_to_response`` which is a + shortcut function. If you want more control over the response, use + the ``render_template`` function, also present in + :ref:`template_module`. You may then create your own :term:`WebOb` + Response object, using the result of ``render_template`` as the + response's body. ``models.py`` ~~~~~~~~~~~~~ -In our sample app, the ``models.py`` module provides the model data. -We create an interface ``IMyModel`` that gives us the "type" for our -data. We then write a class ``MyModel`` that provides the behavior -for instances of the ``IMyModel`` type. +The ``models.py`` module provides the :term:`model` data for our +application. We write a class named ``MyModel`` that provides the +behavior. We then create an interface ``IMyModel`` that is a +:term:`interface` which serves as the "type" for our data, and we +associate it without our ``MyModel`` class by claiming that the class +``implements`` the interface. .. literalinclude:: myproject/myproject/models.py :linenos: @@ -303,29 +326,39 @@ make any assumption about which sort of datastore you'll want to use, so the sample application uses an instance of ``MyModel`` to represent the root. +What will likely frighten new developers in the model file is the use +of :term:`interface` classes. In their simplest form (which is the +only form that :mod:`repoze.bfg` requires you to understand), +interfaces are simply "marker" attributes indicating the *type* of a +model object. These can be attached to classes (via the +``implements`` function with one or more interfaces as arguments at +class scope). In more advanced usage, they can be attached directly +to instances. We do not demonstrate that here. + ``run.py`` ~~~~~~~~~~ We need a small Python module that configures our application and -advertises itself to our Paste ``.ini`` file. For convenience, we -also make it possible to run this module directory without the Paste -configuration file: +advertises itself to our :term:`PasteDeploy` ``.ini`` file. For +convenience, we also make it possible to run this module directory +without the PasteDeploy configuration file: .. literalinclude:: myproject/myproject/run.py :linenos: #. Lines 1 - 7 define a function that returns a :mod:`repoze.bfg` - Router application. This is meant to be called by the PasteDeploy - framework as a result of running ``paster serve``. + Router application from :ref:`router_module` . This is meant to be + called by the :term:`PasteDeploy` framework as a result of running + ``paster serve``. -#. Lines 9 - 12 allow this file to serve as a shortcut for executing - our program if the ``run.py`` file is executed directly. It starts - our application under a web server on port 5432. +#. Lines 9 - 12 allow this file to serve optionally as a shortcut for + executing our program if the ``run.py`` file is executed directly. + It starts our application under a web server on port 5432. ``templates/mytemplate.pt`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The single template in the project looks like so: +The single :term:`template` in the project looks like so: .. literalinclude:: myproject/myproject/templates/mytemplate.pt :linenos: @@ -333,7 +366,8 @@ The single template in the project looks like so: This is a :term:`z3c.pt` template. It displays the current project name when it is rendered. It is referenced by the ``my_view`` -function in the ``views.py`` module. +function in the ``views.py`` module. Templates are accessed and used +by view functions. ``tests.py`` ~~~~~~~~~~~~ @@ -345,5 +379,7 @@ The ``tests.py`` module includes unit tests for your application. This sample ``tests.py`` file has a single unit test defined within it. This is the code that is executed when you run ``setup.py test --q``. You may add more tests here as you build your application. +-q``. You may add more tests here as you build your application. You +are not required to write tests to use :mod:`repoze.bfg`, this file is +simply provided as convenience and example. diff --git a/docs/narr/security.rst b/docs/narr/security.rst index 7adeda3b9..36c0b618a 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -1,10 +1,10 @@ Security ======== -``repoze.bfg`` provides an optional declarative security system that -prevents views that are protected by a :term:`permission` from being -rendered when the user represented by the request does not have the -appropriate level of access in a context. +:mod:`repoze.bfg` provides an optional declarative security system +that prevents views that are protected by a :term:`permission` from +being rendered when the user represented by the request does not have +the appropriate level of access in a context. Security is enabled by adding configuration to your ``configure.zcml`` which specifies a :term:`security policy`. @@ -12,8 +12,8 @@ which specifies a :term:`security policy`. Enabling a Security Policy -------------------------- -By default, ``repoze.bfg`` enables no security policy. All views are -accessible by completely anonymous users. +By default, :mod:`repoze.bfg` enables no security policy. All views +are accessible by completely anonymous users. However, if you add the following bit of code to your application's ``configure.zcml``, you will enable a security policy:: @@ -36,10 +36,11 @@ WSGI server. Protecting Views with Permissions --------------------------------- -You declaratively protected a particular view with a permisson via the -``configure.zcml`` application registry. For example, the following -declaration protects the view named ``add_entry.html`` when invoked -against an ``IBlog`` context with the ``add`` permission:: +You declaratively protected a particular view with a +:term:`permission` via the ``configure.zcml`` application registry. +For example, the following declaration protects the view named +``add_entry.html`` when invoked against an ``IBlog`` context with the +``add`` permission:: <bfg:view for=".models.IBlog" @@ -59,7 +60,7 @@ to the system. You can name permissions whatever you like. Assigning ACLs to your Model Objects ------------------------------------ -When ``repoze.bfg`` determines whether a user possesses a particular +When :mod:`repoze.bfg` determines whether a user possesses a particular permission in a :term:`context`, it examines the :term:`ACL` associated with the context. An ACL is associated with a context by virtue of the ``__acl__`` attribute of the model object representing @@ -86,9 +87,10 @@ class:: ] implements(IBlog, ILocation) -The above ACL indicates that the Everyone principal (a system-defined -principal) is allowed to view the blog, the ``group:editors`` -principal is allowed to add to and edit the blog. +The above ACL indicates that the ``Everyone`` principal (a special +system-defined principal indicating, literally, everyone) is allowed +to view the blog, the ``group:editors`` principal is allowed to add to +and edit the blog. A principal is usually a user id, however it also may be a group id if your authentication system provides group information and the security @@ -119,7 +121,7 @@ which points at their parent object. The root object's ``__parent__`` is ``None``. An object with a ``__parent__`` attribute and a ``__name__`` attribute is said to be *location-aware*. -If the root object in a ``repoze.bfg`` application declares that it +If the root object in a :mod:`repoze.bfg` application declares that it implements the ``ILocation`` interface, it is assumed that the objects in the rest of the model are location-aware. Even if they are not explictly, if the root object is marked as ``ILocation``, the bfg diff --git a/docs/narr/traversal.rst b/docs/narr/traversal.rst index 64e92c570..673f94927 100644 --- a/docs/narr/traversal.rst +++ b/docs/narr/traversal.rst @@ -1,33 +1,30 @@ +.. _traversal_chapter: + Traversal ========= -In many popular web frameworks, a "URL dispatcher" is used to +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 "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 +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 <http://www.djangoproject.com/documentation/url_dispatch/>`_ and the -`Routes URL mapping system <http://routes.groovie.org/>`_ . - -It is possible, however, to map URLs to code differently, using -"object graph traversal". The venerable Zope and CherryPy web -frameworks offer graph-traversal-based URL dispatch. -:mod:`repoze.bfg` also provides graph-traversal-based dispatch of URLs -to code. Graph-traversal based dispatching is useful if you like the -URL to represent an arbitrary hierarchy of potentially heterogeneous -items. +:term:`Routes` URL mapping system. -.. note:: +:mod:`repoze.bfg` supports :term:`URL dispatch` via :term:`Routes`. +See the :ref:`urldispatch_module` for more information about using URL +dispatch. - :mod:`repoze.bfg` features graph traversal. However, via the - inclusion of :term:`Routes`, URL dispatch is also supported for the - parts of your URL space that better fit that model. 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. -Non-graph traversal based URL dispatch can easily handle URLs such as +: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. @@ -39,18 +36,18 @@ sets of URLs such as:: ...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 +*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 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". +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 @@ -62,10 +59,10 @@ 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 Zope -(and comfortable with hierarchical data stores like ZODB), mapping a -URL to a graph traversal is a natural way to think about creating a -web application. +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 @@ -74,30 +71,32 @@ stored in a relational database. However, when you have a hierarchical data store, it can provide advantages over using URL-based dispatch. -Thus :mod:`repoze.bfg` provides support for both approaches, even -though the focus is on object graph traversal. +:mod:`repoze.bfg` provides support for both approaches. Graph +traversal is described in detail below. The Model Graph --------------- -Users interact with your :mod:`repoze.bfg`-based application via a +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 a mapping object, such as a Python -dictionary. In fact, all items contained in the graph are either leaf -nodes (these have no ``__getitem__``) or container nodes (these do -have a ``__getitem__``). +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__``). Items contained within the graph are analogous to the concept of -``model`` objects used by many other frameworks (and :mod:`repoze.bfg` -refers to them as models, as well). They are typically instances of -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__``. +: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 -*context*. It then attempts to find a *view* based on the type of the -context. +:term:`context`. It then attempts to find a :term`view` based on the +type (specified by an :term:`interface`) of the context. How :mod:`repoze.bfg` Processes a Request Using Traversal --------------------------------------------------------- @@ -107,12 +106,12 @@ application, the system uses this algorithm to determine which Python code to execute: 1. The request for the page is presented to :mod:`repoze.bfg`'s - "router" in terms of a standard WSGI request, which is + "router" in terms of a standard :term:`WSGI` request, which is represented by a WSGI environment and a ``start_response`` callable. - 2. The router creates a `WebOb <http://pythonpaste.org/webob/>`_ - request object based on the WSGI environment. + 2. The router creates a :term:`WebOb` request object based on the + WSGI environment. 3. The router uses the WSGI environment's ``PATH_INFO`` variable to determine the path segments to traverse. The leading slash is @@ -121,59 +120,60 @@ code to execute: request with a ``PATH_INFO`` variable of ``/a/b/c`` maps to the traversal sequence ``['a', 'b', 'c']``. - 4. Traversal begins at the root object. For the traversal sequence - ``['a', 'b', 'c']``, the root object's ``__getitem__`` is called - with the name ``a``. Traversal continues through the sequence. - In our example, if the root object's ``__getitem__`` called with - the name ``a`` returns an object (aka "object A"), that object's - ``__getitem__`` is called with the name ``b``. If object A - returns an object when asked for ``b``, object B's + 4. :term:`Traversal` begins at the root object. For the traversal + sequence ``['a', 'b', 'c']``, the root object's ``__getitem__`` + is called with the name ``a``. Traversal continues through the + sequence. In our example, if the root object's ``__getitem__`` + called with the name ``a`` returns an object (aka "object A"), + that object's ``__getitem__`` is called with the name ``b``. If + object A returns an object when asked for ``b``, object B's ``__getitem__`` is then asked for the name ``c``, and may return object C. 5. Traversal ends when a) the entire path is exhausted or b) when - any graph element raises a KeyError from its ``__getitem__`` or - c) when any non-final path element traversal does not have a - ``__getitem__`` method (resulting in a NameError) or d) when any - path element is prefixed with the set of characters ``@@`` + any graph element raises a ``KeyError`` from its ``__getitem__`` + or c) when any non-final path element traversal does not have a + ``__getitem__`` method (resulting in a ``NameError``) or d) when + any path element is prefixed with the set of characters ``@@`` (indicating that the characters following the ``@@`` token should be treated as a "view name"). 6. When traversal ends for any of the reasons in the previous step, the the last object found during traversal is deemed to be the - "context". If the path has been exhausted when traversal ends, - the "view name" is deemed to be the empty string (``''``). + :term:`context`. If the path has been exhausted when traversal + ends, the "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 as the view name. Any subseqent path elements after the view name are deemed the - "subpath". For instance, if ``PATH_INFO`` was ``/a/b`` and the + *subpath*. For instance, if ``PATH_INFO`` was ``/a/b`` and the root returned an "A" object, and the "A" object returned a "B" object, 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']``. + 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']``. - 7. If a security policy is configured, the router performs a + 7. If a :term:`security policy` is configured, the router performs a permission lookup. If a permission declaration is found for the view name and context implied by the current request, the security policy is consulted to see if the "current user" (also determined by the security policy) can perform the action. If he - can, processing continues. If he cannot, an HTTPUnauthorized + can, processing continues. If he cannot, an ``HTTPUnauthorized`` error is raised. 8. Armed with the context, the view name, and the subpath, the router performs a view lookup. It attemtps to look up a view - from the :mod:`repoze.bfg` application registry using the view - name and the context. If a view function is found, it is called - with the context and the request. It returns a response, which - is fed back upstream. If a view is not found, a generic WSGI - ``NotFound`` application is constructed. + from the :mod:`repoze.bfg` :term:`application registry` using the + view name and the context. If a view function is found, it is + called with the context and the request. It returns a response, + which is fed back upstream. If a view is not found, a generic + WSGI ``NotFound`` application is constructed. -In either case, the result is returned upstream via the WSGI protocol. +In either case, the result is returned upstream via the :term:`WSGI` +protocol. A Traversal Example ------------------- @@ -209,19 +209,20 @@ error condition. It signifies that: - the "subpath" is ``['biz', 'buz.txt']`` -Because it's the "context", bfg examimes "baz" to find out what "type" +Because it's the "context", bfg examimes "bar" to find out what "type" it is. Let's say it finds that the context is an ``IBar`` type (because "bar" happens to have an attribute attached to it that indicates it's an ``IBar``). -Using the "view name" ("baz") and the type, it asks the "application -registry" (configured separately, via "configure.zcml") this question: +Using the "view name" ("baz") and the type, it asks the +:term:`application registry` (configured separately, via +``configure.zcml``) this question: - - Please find me a "view" (controller in some religions) with the - name "baz" that can be used for the type ``IBar``. + - Please find me a :term:`view` (aka *controller* in some religions) + with the name "baz" that can be used for the type ``IBar``. -Let's say it finds no matching view type. It then returns a NotFound. -The request ends. Everyone is sad. +Let's say it finds no matching view type. It then returns a +``NotFound``. The request ends. Everyone is sad. But! For this graph:: @@ -246,7 +247,7 @@ The user asks for ``http://example.com/foo/bar/baz/biz/buz.txt`` - bfg traverses biz, and attemtps to find "buz.txt" which it does not find. -The fact that it does not find "biz.txt" at this point does not +The fact that it does not find "buz.txt" at this point does not signify an error condition. It signifies that: - the "context" is biz (the context is the last item found during traversal). @@ -261,16 +262,16 @@ it is. Let's say it finds that the context an ``IBiz`` type (because indicates it's an ``IBiz``). Using the "view name" ("buz.txt") and the type, it asks the -"application registry" (configured separately, in "configure.zcml") -this question: +:term:`application registry` this question: - - Please find me a "view" (controller in some religions) with the - name "buz.txt" that can be used for type ``IBiz``. + - Please find me a :term:`view` (*controller* in some religions) + with the name "buz.txt" that can be used for type ``IBiz``. Let's say that question is answered "here you go, here'a a bit of code -that is willing to deal with that case", and returns a view. It is -passed the "biz" object as the "context" and the current WebOb request -as the "request". It returns a response. +that is willing to deal with that case", and returns a :term:`view`. +It is passed the "biz" object as the "context" and the current +:term:`WebOb` :term:`request` as the "request". It returns a +term:`response`. There are two special cases: |
