summaryrefslogtreecommitdiff
path: root/docs/tutorials
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-06-01 00:15:14 +0000
committerChris McDonough <chrism@agendaless.com>2009-06-01 00:15:14 +0000
commit6117c04a79d29b710391d95c5cce63358c5490fe (patch)
tree86ada0eee517b2a2ae53c51604d444e1183b30d0 /docs/tutorials
parentc2dbab3e024be02c4246746f3d7d9dc5da9219d8 (diff)
downloadpyramid-6117c04a79d29b710391d95c5cce63358c5490fe.tar.gz
pyramid-6117c04a79d29b710391d95c5cce63358c5490fe.tar.bz2
pyramid-6117c04a79d29b710391d95c5cce63358c5490fe.zip
Keep useful stuff.
Diffstat (limited to 'docs/tutorials')
-rw-r--r--docs/tutorials/bfgwiki2/authorization.rst270
-rw-r--r--docs/tutorials/bfgwiki2/basiclayout.rst129
-rw-r--r--docs/tutorials/bfgwiki2/definingmodels.rst147
-rw-r--r--docs/tutorials/bfgwiki2/definingviews.rst364
-rw-r--r--docs/tutorials/bfgwiki2/distributing.rst96
-rw-r--r--docs/tutorials/bfgwiki2/index.rst15
-rw-r--r--docs/tutorials/bfgwiki2/installation.rst3
-rw-r--r--docs/tutorials/bfgwiki2/viewdecorators.rst257
8 files changed, 90 insertions, 1191 deletions
diff --git a/docs/tutorials/bfgwiki2/authorization.rst b/docs/tutorials/bfgwiki2/authorization.rst
deleted file mode 100644
index 437cb9c74..000000000
--- a/docs/tutorials/bfgwiki2/authorization.rst
+++ /dev/null
@@ -1,270 +0,0 @@
-====================
-Adding Authorization
-====================
-
-Our application currently allows anyone with access to the server to
-view, edit, and add pages to our wiki. For purposes of demonstration
-we'll change our application to allow people whom possess a specific
-username (`editor`) to add and edit wiki pages but we'll continue
-allowing anyone with access to the server to view pages.
-:mod:`repoze.bfg` provides facilities for *authorization* and
-*authentication*. We'll make use of both features to provide security
-to our application.
-
-Configuring a ``repoze.bfg`` Authentication Policy
---------------------------------------------------
-
-For any :mod:`repoze.bfg` application to perform authorization, we
-need to change our ``run.py`` module to add an :term:`authentication
-policy`. Adding an authentication policy actually causes the system
-to begin to use :term:`authorization`.
-
-Changing ``run.py``
-~~~~~~~~~~~~~~~~~~~
-
-Change your ``run.py`` module to import the
-``AuthTktAuthenticationPolicy`` from ``repoze.bfg.authentication``.
-Within the body of the ``make_app`` function, construct an instance of
-the policy, and pass it as the ``authentication_policy`` argument to
-the ``make_app`` function. The first positional argument of an
-``AuthTktAuthenticationPolicy`` is a secret used to encrypt cookie
-data. Its second argument ("callback") should be a callable that
-accepts a userid. If the userid exists in the system, the callback
-should return a sequence of group identifiers (or an empty sequence if
-the user isn't a member of any groups). If the userid *does not*
-exist in the system, the callback should return ``None``. We'll use
-"dummy" data to represent user and groups sources. When we're done,
-your application's ``run.py`` will look like this.
-
-.. literalinclude:: src/authorization/tutorial/run.py
- :linenos:
- :language: python
-
-BFG's ``make_app`` callable also can accept an authorization policy
-parameter. We don't need to specify one, we'll use the default.
-
-Adding Login and Logout Views
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-We'll add a ``login`` view which renders a login form and processes
-the post from the login form, checking credentials.
-
-We'll also add a ``logout`` view to our application and provide a link
-to it. This view will clear the credentials of the logged in user and
-redirect back to the front page.
-
-We'll add a different file (for presentation convenience) to add login
-and logout views. Add a file named ``login.py`` to your application
-(in the same directory as ``views.py``) with the following content:
-
-.. literalinclude:: src/authorization/tutorial/login.py
- :linenos:
- :language: python
-
-Changing Existing Views
-~~~~~~~~~~~~~~~~~~~~~~~
-
-Then we need to change each opf our ``view_page``, ``edit_page`` and
-``add_page`` views in ``views.py`` to pass a "logged in" parameter
-into its template. We'll add something like this to each view body:
-
-.. code-block:: python
- :linenos:
-
- logged_in = authenticated_user(request)
-
-We'll then change the return value of ``render_template_to_response``
-to pass the `resulting `logged_in`` value to the template, e.g.:
-
-.. code-block:: python
- :linenos:
-
- return render_template_to_response('templates/view.pt',
- request = request,
- page = context,
- content = content,
- logged_in = logged_in,
- edit_url = edit_url)
-
-Adding the ``login.pt`` Template
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Add a ``login.pt`` template to your templates directory. It's
-referred to within the login view we just added to ``login.py``.
-
-.. literalinclude:: src/authorization/tutorial/templates/login.pt
- :linenos:
- :language: xml
-
-Change ``view.pt`` and ``edit.pt``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-We'll also need to change our ``edit.pt`` and ``view.pt`` templates to
-display a "Logout" link if someone is logged in. This link will
-invoke the logout view.
-
-To do so we'll add this to both templates within the ``<div
-class="main_content">`` div:
-
-.. code-block:: xml
- :linenos:
-
- <span tal:condition="logged_in"><a href="${request.application_url}/logout">Logout</a></span>
-
-Changing ``configure.zcml``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Change your application's ``configure.zcml`` to add a slightly
-inscrutable ``utility`` stanza. This configures our login view to
-show up when BFG detects that a view invocation can not be authorized.
-When you're done, your ``configure.zcml`` will look like so:
-
-.. literalinclude:: src/authorization/tutorial/configure.zcml
- :linenos:
- :language: xml
-
-Giving Our Root Model Object an ACL
------------------------------------
-
-We need to give our root model object an ACL. This ACL will be
-sufficient to provide enough information to the BFG security machinery
-to challenge a user who doesn't have appropriate credentials when he
-attempts to invoke the ``add_page`` or ``edit_page`` views.
-
-We need to perform some imports at module scope in our ``models.py``
-file:
-
-.. code-block:: python
- :linenos:
-
- from repoze.bfg.security import Allow
- from repoze.bfg.security import Everyone
-
-Our root model is a ``Wiki`` object. We'll add the following line at
-class scope to our ``Wiki`` class:
-
-.. code-block:: python
- :linenos:
-
- __acl__ = [ (Allow, Everyone, 'view'), (Allow, 'editor', 'edit') ]
-
-It's only happenstance that we're assigning this ACL at class scope.
-An ACL can be attached to an object *instance* too; this is how "row
-level security" can be achieved in :mod:`repoze.bfg` applications. We
-actually only need *one* ACL for the entire system, however, because
-our security requirements are simple, so this feature is not
-demonstrated.
-
-Our resulting ``models.py`` file will now look like so:
-
-.. literalinclude:: src/authorization/tutorial/models.py
- :linenos:
- :language: python
-
-Adding ``permission`` Declarations to our ``bfg_view`` Decorators
------------------------------------------------------------------
-
-To protect each of our views with a particular permission, we need to
-pass a ``permission`` argument to each of our ``bfg_view`` decorators.
-To do so, within ``views.py``:
-
-- We add ``permission='view'`` to the ``bfg_view`` decorator attached
- to the ``static_view`` view function. This makes the assertion that
- only users who possess the effective ``view`` permission at the time
- of the request may invoke this view. We've granted ``Everyone`` the
- view permission at the root model via its ACL, so everyone will be
- able to invoke the ``static_view`` view.
-
-- We add ``permission='view'`` to the ``bfg_view`` decorator attached
- to the ``view_wiki`` view function. This makes the assertion that
- only users who possess the effective ``view`` permission at the time
- of the request may invoke this view. We've granted ``Everyone`` the
- view permission at the root model via its ACL, so everyone will be
- able to invoke the ``view_wiki`` view.
-
-- We add ``permission='view'`` to the ``bfg_view`` decorator attached
- to the ``view_page`` view function. This makes the assertion that
- only users who possess the effective ``view`` permission at the time
- of the request may invoke this view. We've granted ``Everyone`` the
- view permission at the root model via its ACL, so everyone will be
- able to invoke the ``view_page`` view.
-
-- We add ``permission='edit'`` to the ``bfg_view`` decorator attached
- to the ``add_page`` view function. This makes the assertion that
- only users who possess the effective ``view`` permission at the time
- of the request may invoke this view. We've granted ``editor`` the
- view permission at the root model via its ACL, so only the user
- named ``editor`` will able to invoke the ``add_page`` view.
-
-- We add ``permission='edit'`` to the ``bfg_view`` decorator attached
- to the ``edit_page`` view function. This makes the assertion that
- only users who possess the effective ``view`` permission at the time
- of the request may invoke this view. We've granted ``editor`` the
- view permission at the root model via its ACL, so only the user
- named ``editor`` will able to invoke the ``edit_page`` view.
-
-Viewing the Application in a Browser
-------------------------------------
-
-Once we've set up the WSGI pipeline properly, we can finally examine
-our application in a browser. The views we'll try are as follows:
-
-- Visiting `http://localhost:6543/ <http://localhost:6543/>`_ in a
- browser invokes the ``view_wiki`` view. This always redirects to
- the ``view_page`` view of the FrontPage page object. It is
- executable by any user.
-
-- Visiting `http://localhost:6543/FrontPage/
- <http://localhost:6543/FrontPage/>`_ in a browser invokes the
- ``view_page`` view of the front page page object. This is because
- it's the *default view* (a view without a ``name``) for Page
- objects. It is executable by any user.
-
-- Visiting `http://localhost:6543/FrontPage/edit_page
- <http://localhost:6543/FrontPage/edit_page>`_ in a browser invokes
- the edit view for the front page object. It is executable by only
- the ``editor`` user. If a different user (or the anonymous user)
- invokes it, a login form will be displayed. Supplying the
- credentials with the username ``editor``, password ``editor`` will
- show the edit page form being displayed.
-
-- Visiting `http://localhost:6543/add_page/SomePageName
- <http://localhost:6543/add_page/SomePageName>`_ in a browser invokes
- the add view for a page. It is executable by only the ``editor``
- user. If a different user (or the anonymous user) invokes it, a
- login form will be displayed. Supplying the credentials with the
- username ``editor``, password ``editor`` will show the edit page
- form being displayed.
-
-Seeing Our Changes To ``views.py`` and our Templates
-----------------------------------------------------
-
-Our ``views.py`` module will look something like this when we're done:
-
-.. literalinclude:: src/authorization/tutorial/views.py
- :linenos:
- :language: python
-
-Our ``edit.pt`` template will look something like this when we're done:
-
-.. literalinclude:: src/authorization/tutorial/templates/edit.pt
- :linenos:
- :language: xml
-
-Our ``view.pt`` template will look something like this when we're done:
-
-.. literalinclude:: src/authorization/tutorial/templates/view.pt
- :linenos:
- :language: xml
-
-Revisiting the Application
----------------------------
-
-When we revisit the application in a browser, and log in (as a result
-of hitting an edit or add page and submitting the login form with the
-``editor`` credentials), we'll see a Logout link in the upper right
-hand corner. When we click it, we're logged out, and redirected back
-to the front page.
-
-
-
diff --git a/docs/tutorials/bfgwiki2/basiclayout.rst b/docs/tutorials/bfgwiki2/basiclayout.rst
index 1ec10094e..7cf8274c8 100644
--- a/docs/tutorials/bfgwiki2/basiclayout.rst
+++ b/docs/tutorials/bfgwiki2/basiclayout.rst
@@ -2,9 +2,9 @@
Basic Layout
============
-The starter files generated by the ``bfg_zodb`` template are basic,
-but they provide a good orientation for the high-level patterns common
-to most :term:`traversal` -based BFG (and BFG with ZODB) projects.
+The starter files generated by the ``bfg_routesalchemy`` template are
+basic, but they provide a good orientation for the high-level patterns
+common to most :term:`url dispatch` -based BFG projects.
``__init__.py``
---------------
@@ -16,9 +16,9 @@ directory as a Python package.
Configuration With ``configure.zcml``
--------------------------------------
-BFG uses a markup language syntactically the same as Zope's
-implementation of :term:`ZCML`, but using a different default XML
-namespace. Our sample ZCML file looks like the following:
+BFG uses a configuration markup language syntactically the same as
+Zope's implementation of :term:`ZCML`, but using a different default
+XML namespace. Our sample ZCML file looks like the following:
.. literalinclude:: src/basiclayout/tutorial/configure.zcml
:linenos:
@@ -28,26 +28,29 @@ namespace. Our sample ZCML file looks like the following:
#. *Line 3*. Boilerplate, the comment explains.
-#. *Lines 6-9*. Register a ``<view>`` that is bound to a class.
- ``.views.my_view`` is a *function* we write (generated by the
- ``bfg_zodb`` template) that is given a ``context`` and a
- ``request`` and returns a response.
+#. *Lines 6-7*. Register a :term:`subscriber` that tears down the
+ SQLAlchemy connection after a request is finished.
- Since this ``<view>`` doesn't have a ``name`` attribute, it is the
- "default" view for that class.
+#. *Lines 9-12*. Register a ``<route>`` that will be used when the
+ URL is ``/``. Since this ``<view>`` has a blank ``name``
+ attribute, it is the "default" view. ``.views.my_view`` refers to a
+ *function* we write (generated by the ``bfg_routesalchemy``
+ template) that is given a ``context`` and a ``request`` and returns
+ a response.
-#. *Lines 11-15*. Register a view on the ``MyModels`` class that
- answers URL segments of ``static``. This is a view that will serve
- up static resources for us, in this case, at
+#. *Lines 14-17*. Register a ``<route>`` with a path that starts with
+ ``/static``, capturing the rest of the URL as ``subpath``. This is
+ a view that will serve up static resources for us, in this case, at
``http://localhost:6543/static/`` and below.
Content Models with ``models.py``
---------------------------------
-BFG often uses the word *model* when talking about content resources
-arranged in a hierarchical *model graph*. The ``models.py`` file is
-where the ``bfg_zodb`` Paster template put the classes that implement
-our models.
+In the context of a SQLAlchemy-based application, a *model* object is
+an object composed by quering the SQL database which backs an
+application. SQLAlchemy is an "object relational mapper" (an ORM).
+The ``models.py`` file is where the ``bfg_zodb`` Paster template put
+the classes that implement our models.
Here is the source for ``models.py``:
@@ -55,22 +58,36 @@ Here is the source for ``models.py``:
:linenos:
:language: py
-#. *Lines 3-4*. The ``MyModel`` class we referred to in the ZCML is
- implemented here. It is persistent (via PersistentMapping). The
- ``__parent__`` and ``__name__`` are important parts of the
- traversal protocol. By default, have these as ``None`` indicating
- that this is the :term:`root` object.
-
-#. *Lines 6-12*. ``appmaker`` is used to return the *application
- root* object. It is called on *every request* to the BFG
- application (it is essentially a :term:`root factory`). It also
- performs bootstrapping by *creating* an application root (inside
- the ZODB root object) if one does not already exist.
-
- We do so by first seeing if the database has the persistent
- application root. If not, we make an instance, store it, and
- commit the transaction. We then return the application root
- object.
+#. *Lines 1-16*. Imports to support later code.
+
+#. *Line 18*. We set up a SQLAlchemy "DBSession" object here. We
+ specify that we'd like to use the "ZopeTransactionExtension". This
+ extension is an extension which allows us to use a *transaction
+ manager* instead of controlling commits and aborts to database
+ operations by hand.
+
+#. *Line 20*. Set up a SQLAlchemy metadata object.
+
+#. *Lines 22-24*. A model class named ``Model``. It has an
+ ``__init__`` that takes a single argument (``name``). It stores a
+ single attribute named ``name``.
+
+#. *Lines 26-31*. A SQLAlchemy ``Table`` declaration named
+ ``models_table`` which we'll use later to map onto our ``Model``
+ class.
+
+#. *Line 33*. We map our ``models_table`` table to our Models class
+ here. This makes an association between the ``Model`` class and
+ the ``models`` table in the database, as far as SQLAlchemy is
+ concerned.
+
+#. *Lines 35-40*. A function named ``populate`` which adds a single
+ model instance into our SQL storage and commits a transaction.
+
+#. *Lines 42-50*. A function named ``initialize_sql`` which sets up
+ an actual SQL database and binds it to our SQLAlchemy DBSession
+ object. It also calls the ``populate`` function, to do initial
+ database population.
App Startup with ``run.py``
---------------------------
@@ -84,21 +101,33 @@ and its ``app`` function:
:linenos:
:language: py
-#. *Line 11*. After importing our application, get the ``appmaker``
- function described above.
-
-#. *Line 12*. Get the ZODB configuration from the ``tutorial.ini``
- file's ``[app:main]`` section. This will be a URI (something like
- ``file:///path/to/Data.fs``).
-
-#. Line *16*. We create a :term:`root factory` using the
- ``PersistentApplicationFinder`` helper class, passing it the
- ZODB URI and our appmaker.
-
-#. Line *17*. We use the ``repoze.bfg.router.make_app`` to return a
- :term:`WSGI` application. The ``make_app`` function takes the root
- factory (``get_root``), the *package* representing our application,
- and the keywords parsed by PasteDeploy.
+#. *Lines 1-5*. Imports to support later code.
+
+#. *Lines 7-11*. We define a ``Cleanup`` class which has a
+ ``__del__`` method (the method called at Python object
+ destruction), which calls a function.
+
+#. *Lines 13-15*. An event :term:`subscriber` which adds a
+ ``Cleanup`` instance to the WSGI environment as
+ ``tutorial.sasession``. As a result of registering this event
+ subscriber, when the WSGI environment is cleaned up, our database
+ connection will be removed.
+
+#. *Lines 17-24*. Get the database configuration string from the
+ ``tutorial.ini`` file's ``[app:sql]`` section. This will be a URI
+ (something like ``sqlite://``).
+
+#. Line *25*. We initialize our SQL database using SQLAlchemy, passing
+ it the db string.
+
+#. Line *26*. We use the ``repoze.bfg.router.make_app`` to return a
+ :term:`WSGI` application. The ``make_app`` function's first
+ parameter is the "root factory". Since this is a URL dispatch
+ application, the root factory is ``None`` (we don't do any
+ :term:`traversal` in this app. The second argument is the
+ *package* representing our application, and the third argument,
+ ``options`` is passed as a keyword argument. It contains a
+ dictionary of options parsed by PasteDeploy.
We'll later change ``run.py`` when we add :term:`authorization` to our
wiki application.
diff --git a/docs/tutorials/bfgwiki2/definingmodels.rst b/docs/tutorials/bfgwiki2/definingmodels.rst
deleted file mode 100644
index 61eb5f112..000000000
--- a/docs/tutorials/bfgwiki2/definingmodels.rst
+++ /dev/null
@@ -1,147 +0,0 @@
-===============
-Defining Models
-===============
-
-The first change we'll make to our bone-stock paster-generated
-application will be to define a number of :term:`model` constructors.
-For this application, which will be a Wiki, we will need two kinds of
-model constructors: a "Wiki" model constructor, and a "Page" model
-constructor. Both our Page and Wiki constructors will be class
-objects. A single instance of the "Wiki" class will serve as a
-container for "Page" objects, which will be instances of the "Page"
-class.
-
-Adding Model Classes
---------------------
-
-The first thing we want to do is remove the ``MyModel`` class from the
-generated ``models.py`` file. The ``MyModel`` class is only a sample
-and we're not going to use it.
-
-.. note::
-
- There is nothing automagically special about the filename
- ``models.py``. A project may have many models throughout its
- codebase in arbitrarily-named files. Files implementing models
- often have ``model`` in their filenames (or they may live in a
- Python subpackage of your application package named ``models``) ,
- but this is only by convention.
-
-Then, we'll add a ``Wiki`` class. Because this is a ZODB application,
-this class should inherit from
-``persistent.mapping.PersistentMapping``. We want it to inherit from
-the ``PersistentMapping`` class because our Wiki class will be a
-mapping of wiki page names to ``Page`` objects. The
-``PersistentMapping`` class provides our class with mapping behavior,
-and makes sure that our Wiki page is stored as a "first-class"
-persistent object in our ZODB database.
-
-Our ``Wiki`` class should also have a ``__name__`` attribute set to
-``None`` at class scope, and should have a ``__parent__`` attribute
-set to None at class scope as well. If a model has a ``__parent__``
-attribute of ``None`` in a traversal-based :mod:`repoze.bfg`
-application, it means that it's the :term:`root` model. The
-``__name__`` of the root model is always ``None``.
-
-Then we'll add a ``Page`` class. This class should inherit from
-``persistent.Persistent``. We'll also give it an ``__init__`` method
-that accepts a single parameter named ``data``. This parameter will
-contain the :term:`ReStructuredText` body representing the wiki page
-content. Note that ``Page`` objects don't have an initial
-``__name__`` or ``__parent__`` attribute. All objects in a traversal
-graph must have a ``__name__`` and a ``__parent__`` attribute. We
-don't specify these here because both ``__name__`` and ``__parent__``
-will be set by by a :term:`view` function when a Page is added to our
-Wiki mapping.
-
-Add an Appmaker
----------------
-
-We're using a mini-framework callable named
-``repoze.zodbconn.finder.PersistentApplicationFinder`` in our
-application (see "run.py"). A ``PersistentApplicationFinder`` accepts
-a ZODB URL as well as an "appmaker" callback. This callback typically
-lives in the ``models.py`` file.
-
-We want to change the appmaker function in our ``models.py`` file so
-that our application root is a Wiki instance, and we'll also slot a
-single page object (the front page) into the wiki.
-
-Looking at the Result of Our Edits to ``models.py``
----------------------------------------------------
-
-The result of all of our edits to ``models.py`` will end up looking
-something like this:
-
-.. literalinclude:: src/models/tutorial/models.py
- :linenos:
- :language: python
-
-Testing the Models
-------------------
-
-To make sure the code we just wrote works, we write tests for the
-model classes and the appmaker. Changing ``tests.py``, we'll write a
-separate test class for each model class, and we'll write a test class
-for the ``appmaker``.
-
-To do so, we'll retain the ``tutorial.tests.ViewTests`` class provided
-as a result of the ``bfg_zodb`` project generator but we'll disuse the
-``ViewIntegrationTests`` class. The ``ViewIntegrationTests`` class is
-too "heavy-hammer" for our tastes. We'll add three test classes: one
-for the ``Page`` model named ``PageModelTests``, one for the ``Wiki``
-model named ``WikiModelTests``, and one for the appmaker named
-``AppmakerTests``.
-
-When we're done changing ``tests.py``, it will look something like so:
-
-.. literalinclude:: src/models/tutorial/tests.py
- :linenos:
- :language: python
-
-Running the Tests
------------------
-
-We can run these tests by using ``setup.py test`` in the same way we
-did in :ref:`running_tests`. Assuming our shell's current working
-directory is the "tutorial" distribution directory:
-
-On UNIX:
-
-.. code-block:: bash
-
- $ ../bin/python setup.py test -q
-
-On Windows:
-
-.. code-block:: bash
-
- c:\bigfntut\tutorial> ..\Scripts\python setup.py test -q
-
-The expected output is something like this:
-
-.. code-block:: bash
-
- .....
- ----------------------------------------------------------------------
- Ran 5 tests in 0.008s
-
- OK
-
-Declaring Dependencies in Our ``setup.py`` File
------------------------------------------------
-
-Our application depends on packages which are not dependencies of the
-original "tutorial" application as it was generated by the ``paster
-create`` command. We'll add these dependencies to our ``tutorial``
-package's ``setup.py`` file by assigning these dependencies to both
-the ``install_requires`` and the ``tests_require`` parameters to the
-``setup`` function. In particular, we require the ``docutils``
-package.
-
-Our resulting ``setup.py`` should look like so:
-
-.. literalinclude:: src/models/setup.py
- :linenos:
- :language: python
-
diff --git a/docs/tutorials/bfgwiki2/definingviews.rst b/docs/tutorials/bfgwiki2/definingviews.rst
deleted file mode 100644
index bf47c37ad..000000000
--- a/docs/tutorials/bfgwiki2/definingviews.rst
+++ /dev/null
@@ -1,364 +0,0 @@
-==============
-Defining Views
-==============
-
-Views in BFG are typically simple Python functions that accept two
-parameters: :term:`context`, and :term:`request`. A view is assumed
-to return a :term:`response` object.
-
-Adding View Functions
-=====================
-
-We're going to add four :term:`view` functions to our ``views.py``
-module. One view (named ``view_wiki``) will display the wiki itself
-(it will answer on the root URL), another named ``view_page`` will
-display an individual page, another named ``add_page`` will allow a
-page to be added, and a final view named ``edit_page`` will allow a
-page to be edited.
-
-.. note::
-
- There is nothing automagically special about the filename
- ``views.py``. A project may have many views throughout its codebase
- in arbitrarily-named files. Files implementing views often have
- ``view`` in their filenames (or may live in a Python subpackage of
- your application package named ``views``), but this is only by
- convention.
-
-
-The ``view_wiki`` view function
--------------------------------
-
-The ``view_wiki`` function will respond as the default view of a
-``Wiki`` model object. It always redirects to the ``Page`` object
-named "FrontPage". It returns an instance of the
-``webob.exc.HTTPFound`` class (instances of which implement the WebOb
-:term:`response` interface), and the ``repoze.bfg.model_url`` API.
-``model_url`` constructs a URL to the ``FrontPage`` page
-(e.g. ``http://localhost:6543/FrontPage``), and uses it as the
-"location" of the HTTPFound response, forming an HTTP redirect.
-
-The ``view_page`` view function
--------------------------------
-
-The ``view_page`` function will respond as the default view of a
-``Page`` object. The ``view_page`` function renders the
-:term:`ReStructuredText` body of a page (stored as the ``data``
-attribute of the context, which will be a Page object) as HTML. Then
-it substitutes an HTML anchor for each *WikiWord* reference in the
-rendered HTML using a compiled regular expression.
-
-The curried function named ``check`` is used as the first argument to
-``wikiwords.sub``, indicating that it should be called to provide a
-value for each WikiWord match found in the content. If the wiki (our
-page's ``__parent__``) already contains a page with the matched
-WikiWord name, the ``check`` function generates a view link to be used
-as the substitution value and returns it. If the wiki does not
-already contain a page with with the matched WikiWord name, the
-function generates an "add" link as the subsitution value and returns
-it.
-
-As a result, the ``content`` variable is now a fully formed bit of
-HTML containing various view and add links for WikiWords based on the
-content of our current page object.
-
-We then generate an edit URL (because it's easier to do here than in
-the template), and we call the
-``repoze.bfg.chameleon_zpt.render_template_to_response`` function with
-a number of arguments. The first argument is the *relative* path to a
-:term:`Chameleon` ZPT template. It is relative to the directory of
-the file in which we're creating the ``view_page`` function. The
-``render_template_to_response`` function also accepts ``request``,
-``page``, ``content``, and ``edit_url`` as keyword arguments. As a
-result, the template will be able to use these names to perform
-various rendering tasks.
-
-The result of ``render_template_to_response`` is returned to
-:mod:`repoze.bfg`. Unsurprisingly, it is a response object.
-
-The ``add_page`` view function
-------------------------------
-
-The ``add_page`` function will be invoked when a user clicks on a
-WikiWord which isn't yet represented as a page in the system. The
-``check`` function within the ``view_page`` view generates URLs to
-this view. It also acts as a handler for the form that is generated
-when we want to add a page object. The ``context`` of the
-``add_page`` view is always a Wiki object (*not* a Page object).
-
-The request :term:`subpath` in BFG is the sequence of names that are
-found *after* the view name in the URL segments given to BFG as the
-result of a request. If our add view is invoked via,
-e.g. ``http://localhost:6543/add_page/SomeName``, the :term:`subpath`
-will be ``['SomeName']``.
-
-The add view takes the zeroth element of the subpath (the wiki page
-name), and aliases it to the name attribute in order to know the name
-of the page we're trying to add.
-
-If the view rendering is *not* a result of a form submission (if the
-expression ``'form.submitted' in request.params`` is False), the view
-renders a template. To do so, it generates a "save url" which the
-template use as the form post URL during rendering. We're lazy here,
-so we're trying to use the same template (``templates/edit.pt``) for
-the add view as well as the page edit view, so we create a dummy Page
-object in order to satisfy the edit form's desire to have *some* page
-object exposed as ``page``, and we'll render the template to a
-response.
-
-If the view rendering *is* a result of a form submission (if the
-expression ``'form.submitted' in request.params`` is True), we scrape
-the page body from the form data, create a Page object using the name
-in the subpath and the page body, and save it into "our context" (the
-wiki) using the ``__setitem__`` method of the context. We then
-redirect back to the ``view_page`` view (the default view for a page)
-for the newly created page.
-
-The ``edit_page`` view function
--------------------------------
-
-The ``edit_page`` function will be invoked when a user clicks the
-"Edit this Page" button on the view form. It renders an edit form but
-it also acts as the handler for the form it renders. The ``context``
-of the ``edit_page`` view will *always* be a Page object (never a Wiki
-object).
-
-If the view rendering is *not* a result of a form submission (if the
-expression ``'form.submitted' in request.params`` is False), the view
-simply renders the edit form, passing the request, the page object,
-and a save_url which will be used as the action of the generated form.
-
-If the view rendering *is* a result of a form submission (if the
-expression ``'form.submitted' in request.params`` is True), the view
-grabs the ``body`` element of the request parameter and sets it as the
-``data`` attribute of the page context. It then redirects to the
-default view of the context (the page), which will always be the
-``view_page`` view.
-
-Viewing the Result of Our Edits to ``views.py``
-===============================================
-
-The result of all of our edits to ``views.py`` will leave it looking
-like this:
-
-.. literalinclude:: src/views/tutorial/views.py
- :linenos:
- :language: python
-
-Adding Templates
-================
-
-The views we've added all reference a :term:`template`. Each template
-is a :term:`Chameleon` template. The default templating system in
-:mod:`repoze.bfg` is a variant of :term:`ZPT` provided by Chameleon.
-These templates will live in the ``templates`` directory of our
-tutorial package.
-
-The ``view.pt`` Template
-------------------------
-
-The ``view.pt`` template is used for viewing a single wiki page. It
-is used by the ``view_page`` view function. It should have a div that
-is "structure replaced" with the ``content`` value provided by the
-view. It should also have a link on the rendered page that points at
-the "edit" URL (the URL which invokes the ``edit_page`` view for the
-page being viewed).
-
-Once we're done with the ``view.pt`` template, it will look a lot like
-the below:
-
-.. literalinclude:: src/views/tutorial/templates/view.pt
- :linenos:
- :language: xml
-
-
-The ``edit.pt`` Template
-------------------------
-
-The ``edit.pt`` template is used for adding and editing a wiki page.
-It is used by the ``add_page`` and ``edit_page`` view functions. It
-should display a page containing a form that POSTs back to the
-"save_url" argument supplied by the view. The form should have a
-"body" textarea field (the page data), and a submit button that has
-the name "form.submitted". The textarea in the form should be filled
-with any existing page data when it is rendered.
-
-Once we're done with the ``edit.pt`` template, it will look a lot like
-the below:
-
-.. literalinclude:: src/views/tutorial/templates/edit.pt
- :linenos:
- :language: xml
-
-Static Resources
-----------------
-
-Our templates name a single static resource named ``style.css``. We
-need to create this and place it in a file named ``style.css`` within
-our package's ``templates/static`` directory:
-
-.. literalinclude:: src/views/tutorial/templates/static/style.css
- :linenos:
- :language: css
-
-This CSS file will be accessed via
-e.g. ``http://localhost:6543/static/style.css`` by virtue of the
-``static_view`` view we've defined in the ``views.py`` file. Any
-number and type of static resources can be placed in this directory
-(or subdirectories) and are just referred to by URL within templates.
-
-Testing the Views
-=================
-
-We'll modify our ``tests.py`` file, adding tests for each view
-function we added above. As a result, we'll *delete* the
-``ViewTests`` test in the file, and add four other test classes:
-``ViewWikiTests``, ``ViewPageTests``, ``AddPageTests``, and
-``EditPageTests``. These test the ``view_wiki``, ``view_page``,
-``add_page``, and ``edit_page`` views respectively.
-
-Once we're done with the ``tests.py`` module, it will look a lot like
-the below:
-
-.. literalinclude:: src/views/tutorial/tests.py
- :linenos:
- :language: python
-
-Running the Tests
-=================
-
-We can run these tests by using ``setup.py test`` in the same way we
-did in :ref:`running_tests`. Assuming our shell's current working
-directory is the "tutorial" distribution directory:
-
-On UNIX:
-
-.. code-block:: bash
-
- $ ../bin/python setup.py test -q
-
-On Windows:
-
-.. code-block:: bash
-
-
- c:\bigfntut\tutorial> ..\Scripts\python setup.py test -q
-
-The expected result looks something like:
-
-.. code-block:: bash
-
- .........
- ----------------------------------------------------------------------
- Ran 9 tests in 0.203s
-
- OK
-
-Mapping Views to URLs in ``configure.zcml``
-===========================================
-
-The ``configure.zcml`` file contains ``view`` declarations which serve
-to map URLs (via :term:`traversal`) to view functions. You'll need to
-add five ``view`` declarations to ``configure.zcml``.
-
-#. Add a declaration which maps the "Wiki" class in our ``models.py``
- file to the view named ``static_view`` in our ``views.py`` file with
- the view name ``static``.
-
-#. Add a declaration which maps the "Wiki" class in our ``models.py``
- file to the view named ``view_wiki`` in our ``views.py`` file with
- no view name. This is the default view for a Wiki.
-
-#. Add a declaration which maps the "Page" class in our ``models.py``
- file to the view named ``view_page`` in our ``views.py`` file with
- no view name. This is the default view for a Page.
-
-#. Add a declaration which maps the "Wiki" class in our ``models.py``
- file to the view named ``add_page`` in our ``views.py`` file with
- the view name ``add_page``. This is the add view for a new Page.
-
-#. Add a declaration which maps the "Page" class in our ``models.py``
- file to the view named ``edit_page`` in our ``views.py`` file with
- the view name ``edit_page``. This is the edit view for a page.
-
-As a result of our edits, the ``configure.zcml`` file should look
-something like so:
-
-.. literalinclude:: src/views/tutorial/configure.zcml
- :linenos:
- :language: xml
-
-Examining ``tutorial.ini``
-==========================
-
-Let's take a look at our ``tutorial.ini`` file. The contents of the
-file are as follows:
-
-.. literalinclude:: src/models/tutorial.ini
- :linenos:
- :language: ini
-
-The WSGI Pipeline
------------------
-
-Within ``tutorial.ini``, note the existence of a ``[pipeline:main]``
-section which specifies our WSGI pipeline. This "pipeline" will be
-served up as our WSGI application. As far as the WSGI server is
-concerned the pipeline *is* our application. Simpler configurations
-don't use a pipeline: instead they expose a single WSGI application as
-"main". Our setup is more complicated, so we use a pipeline.
-
-"egg:repoze.zodbconn#closer" is at the "top" of the pipeline. This is
-a piece of middleware which closes the ZODB connection opened by the
-PersistentApplicationFinder at the end of the request.
-
-"egg:repoze.tm#tm" is the second piece of middleware in the pipeline.
-This commits a transaction near the end of the request unless there's
-an exception raised.
-
-Adding an Element to the Pipeline
----------------------------------
-
-Let's add a piece of middleware to the WSGI pipeline.
-"egg:Paste#evalerror" middleware which displays debuggable errors in
-the browser while you're developing (not recommeded for deployment).
-Let's insert evalerror into the pipeline right below
-"egg:repoze.zodbconn#closer", making our resulting ``tutorial.ini``
-file look like so:
-
-.. literalinclude:: src/views/tutorial.ini
- :linenos:
- :language: ini
-
-Viewing the Application in a Browser
-====================================
-
-Once we've set up the WSGI pipeline properly, we can finally examine
-our application in a browser. The views we'll try are as follows:
-
-- Visiting `http://localhost:6543/ <http://localhost:6543/>`_ in a
- browser invokes the ``view_wiki`` view. This always redirects to
- the ``view_page`` view of the FrontPage page object.
-
-- Visiting `http://localhost:6543/FrontPage/
- <http://localhost:6543/FrontPage/>`_ in a browser invokes the
- ``view_page`` view of the front page page object. This is because
- it's the *default view* (a view without a ``name``) for Page objects.
-
-- Visiting `http://localhost:6543/FrontPage/edit_page
- <http://localhost:6543/FrontPage/edit_page>`_ in a browser invokes
- the edit view for the front page object.
-
-- Visiting `http://localhost:6543/add_page/SomePageName
- <http://localhost:6543/add_page/SomePageName>`_ in a browser invokes
- the add view for a page.
-
-- To generate an error, do `http://localhost:6543/add_page
- <http://localhost:6543/add_page>`_. IndexError for
- ``request.subpath[0]``. You'll see an interactive traceback
- facility provided by evalerror.
-
-
-
-
-
diff --git a/docs/tutorials/bfgwiki2/distributing.rst b/docs/tutorials/bfgwiki2/distributing.rst
deleted file mode 100644
index 2b99c9e3a..000000000
--- a/docs/tutorials/bfgwiki2/distributing.rst
+++ /dev/null
@@ -1,96 +0,0 @@
-=============================
-Distributing Your Application
-=============================
-
-Once your application works properly, you can create a "tarball" from
-it by using the ``setup.py sdist`` command. The following commands
-assume your current working directory is the ``tutorial`` package
-we've created and that the parent directory of the ``tutorial``
-package is a virtualenv representing a BFG environment.
-
-On UNIX:
-
-.. code-block:: bash
-
- $ ../bin/python setup.py sdist
-
-On Windows:
-
-.. code-block:: bash
-
- c:\bigfntut> ..\Scripts\python setup.py sdist
-
-.. warning:: If your project files are not checked in to a version
- control repository (such as Subversion), the dist tarball will
- *not* contain all the files it needs to. In particular, it will
- not contain non-Python-source files (such as templates and static
- files). To ensure that these are included, check your files into a
- version control repository before running ``setup.py sdist``.
-
-The output of such a command will be something like:
-
-.. code-block:: bash
-
- running sdist
- running egg_info
- writing requirements to tutorial.egg-info/requires.txt
- writing tutorial.egg-info/PKG-INFO
- writing top-level names to tutorial.egg-info/top_level.txt
- writing dependency_links to tutorial.egg-info/dependency_links.txt
- writing entry points to tutorial.egg-info/entry_points.txt
- writing manifest file 'tutorial.egg-info/SOURCES.txt'
- warning: sdist: missing required meta-data: url
- warning: sdist: missing meta-data: either (author and author_email) or (maintainer and maintainer_email) must be supplied
- creating tutorial-0.1
- creating tutorial-0.1/tutorial
- creating tutorial-0.1/tutorial.egg-info
- creating tutorial-0.1/tutorial/templates
- creating tutorial-0.1/tutorial/templates/static
- creating tutorial-0.1/tutorial/templates/static/images
- making hard links in tutorial-0.1...
- hard linking CHANGES.txt -> tutorial-0.1
- hard linking README.txt -> tutorial-0.1
- hard linking ez_setup.py -> tutorial-0.1
- hard linking setup.cfg -> tutorial-0.1
- hard linking setup.py -> tutorial-0.1
- hard linking tutorial.ini -> tutorial-0.1
- hard linking tutorial/__init__.py -> tutorial-0.1/tutorial
- hard linking tutorial/configure.zcml -> tutorial-0.1/tutorial
- hard linking tutorial/models.py -> tutorial-0.1/tutorial
- hard linking tutorial/run.py -> tutorial-0.1/tutorial
- hard linking tutorial/tests.py -> tutorial-0.1/tutorial
- hard linking tutorial/views.py -> tutorial-0.1/tutorial
- hard linking tutorial.egg-info/PKG-INFO -> tutorial-0.1/tutorial.egg-info
- hard linking tutorial.egg-info/SOURCES.txt -> tutorial-0.1/tutorial.egg-info
- hard linking tutorial.egg-info/dependency_links.txt -> tutorial-0.1/tutorial.egg-info
- hard linking tutorial.egg-info/entry_points.txt -> tutorial-0.1/tutorial.egg-info
- hard linking tutorial.egg-info/not-zip-safe -> tutorial-0.1/tutorial.egg-info
- hard linking tutorial.egg-info/requires.txt -> tutorial-0.1/tutorial.egg-info
- hard linking tutorial.egg-info/top_level.txt -> tutorial-0.1/tutorial.egg-info
- hard linking tutorial/templates/edit.pt -> tutorial-0.1/tutorial/templates
- hard linking tutorial/templates/mytemplate.pt -> tutorial-0.1/tutorial/templates
- hard linking tutorial/templates/view.pt -> tutorial-0.1/tutorial/templates
- hard linking tutorial/templates/static/default.css -> tutorial-0.1/tutorial/templates/static
- hard linking tutorial/templates/static/style.css -> tutorial-0.1/tutorial/templates/static
- hard linking tutorial/templates/static/templatelicense.txt -> tutorial-0.1/tutorial/templates/static
- hard linking tutorial/templates/static/images/img01.gif -> tutorial-0.1/tutorial/templates/static/images
- hard linking tutorial/templates/static/images/img02.gif -> tutorial-0.1/tutorial/templates/static/images
- hard linking tutorial/templates/static/images/img03.gif -> tutorial-0.1/tutorial/templates/static/images
- hard linking tutorial/templates/static/images/img04.gif -> tutorial-0.1/tutorial/templates/static/images
- hard linking tutorial/templates/static/images/spacer.gif -> tutorial-0.1/tutorial/templates/static/images
- copying setup.cfg -> tutorial-0.1
- Writing tutorial-0.1/setup.cfg
- creating dist
- tar -cf dist/tutorial-0.1.tar tutorial-0.1
- gzip -f9 dist/tutorial-0.1.tar
- removing 'tutorial-0.1' (and everything under it)
-
-Note that this command creates a tarball in the "dist" subdirectory
-named ``tutorial-0.1.tar.gz``. You can send this file to your friends
-to show them your cool new application. They should be able to
-install it by pointing the ``easy_install`` command directly at it.
-Or you can upload it to `PyPI <http://pypi.python.org>`_ and share it
-with the rest of the world, where it can be downloaded via
-``easy_install`` remotely like any other package people download from
-PyPI.
-
diff --git a/docs/tutorials/bfgwiki2/index.rst b/docs/tutorials/bfgwiki2/index.rst
index 5860dc181..48126b178 100644
--- a/docs/tutorials/bfgwiki2/index.rst
+++ b/docs/tutorials/bfgwiki2/index.rst
@@ -1,12 +1,13 @@
-.. _bfg_wiki_tutorial:
+.. _bfg_sql_wiki_tutorial:
-BFG Wiki Tutorial
-=================
+BFG URL Dispatch + SQLAlchemy Wiki Tutorial
+===========================================
-This tutorial introduces a :term:`traversal` -based BFG application to
-a developer with at least passing familiarity to Python. When we're
-done with the tutorial, the developer will have created a basic Wiki
-application with authentication.
+This tutorial introduces a :term:`url dispatch` and :term:`SQLAlchemy`
+-based BFG application to a developer with at least passing
+familiarity to Python. When we're done with the tutorial, the
+developer will have created a basic Wiki application with
+authentication.
Contents:
diff --git a/docs/tutorials/bfgwiki2/installation.rst b/docs/tutorials/bfgwiki2/installation.rst
index 58a811e8f..fd96211dc 100644
--- a/docs/tutorials/bfgwiki2/installation.rst
+++ b/docs/tutorials/bfgwiki2/installation.rst
@@ -223,6 +223,9 @@ On Windows:
c:\bigfntut\tutorial> ..\Scripts\nosetests --cover-package=tutorial --cover-erase --with-coverage
+Looks like our package's ``models`` module doesn't quite have 100%
+test coverage.
+
Visit the Application in a Browser
==================================
diff --git a/docs/tutorials/bfgwiki2/viewdecorators.rst b/docs/tutorials/bfgwiki2/viewdecorators.rst
deleted file mode 100644
index b9fd3b454..000000000
--- a/docs/tutorials/bfgwiki2/viewdecorators.rst
+++ /dev/null
@@ -1,257 +0,0 @@
-==========================================================
-Using View Decorators Rather than ZCML ``view`` directives
-==========================================================
-
-So far we've been using :term:`ZCML` to map model types to views.
-It's often easier to use the "bfg_view" view decorator to do this
-mapping. Using view decorators provides better locality of reference
-for the mapping, because you can see which model types and view names
-the view will serve right next to the view function itself. In this
-mode, however, you lose the ability for some views to be overridden
-"from the outside" (by someone using your application as a framework).
-Since this application is *definitely* not a framework, it makes sense
-for us to switch over to using view decorators.
-
-Adding View Decorators
-======================
-
-We're going to import the ``bfg_view`` callable from the
-``repoze.bfg.view`` module. This callable can be used as a function
-decorator. We'll use it to decorate our ``static_view``,
-``view_wiki``, ``view_page``, ``add_page`` and ``edit_page`` view
-functions.
-
-The ``bfg_view`` callable accepts a number of arguments:
-
-``for_``
-
- The model type which this view is "for", in our case a class.
-
-``name``
-
- The name of the view.
-
-There are other arguments which this callable accepts, but these are
-the ones we're going to use.
-
-The ``static_view`` view function
----------------------------------
-
-Because our ``bfg_view`` decorator can only decorate view functions
-and classes (not instances), we rename our ``static_view`` to
-``static_app`` and create a new function named ``static_view`` which
-simply calls ``static_app`` with the context and request. We decorate
-the resulting ``static_view`` function with the following:
-
-.. code-block:: python
- :linenos:
-
- @bfg_view(for_=Wiki, name='static')
-
-This indicates that the view is "for" the Wiki class and has the
-view_name ``static``. After injecting this decorator, we can now
-*remove* the following from our ``configure.zcml`` file:
-
-.. code-block:: xml
- :linenos:
-
- <view
- for=".models.Wiki"
- view=".views.static_view"
- name="static"
- />
-
-Our decorator takes its place.
-
-The ``view_wiki`` view function
--------------------------------
-
-The decorator above the ``view_wiki`` function will be:
-
-.. code-block:: python
- :linenos:
-
- @bfg_view(for_=Wiki)
-
-This indicates that the view is "for" the Wiki class and has the
-*empty* view_name (indicating the default view). After injecting this
-decorator, we can now *remove* the following from our
-``configure.zcml`` file:
-
-.. code-block:: xml
- :linenos:
-
- <view
- for=".models.Wiki"
- view=".views.view_wiki"
- />
-
-Our new decorator takes its place.
-
-The ``view_page`` view function
--------------------------------
-
-The decorator above the ``view_page`` function will be:
-
-.. code-block:: python
- :linenos:
-
- @bfg_view(for_=Page)
-
-This indicates that the view is "for" the Page class and has the
-*empty* view_name (indicating the default view). After injecting this
-decorator, we can now *remove* the following from our
-``configure.zcml`` file:
-
-.. code-block:: xml
- :linenos:
-
- <view
- for=".models.Page"
- view=".views.view_page"
- />
-
-
-Our new decorator takes its place.
-
-The ``add_page`` view function
-------------------------------
-
-The decorator above the ``add_page`` function will be:
-
-.. code-block:: python
- :linenos:
-
- @bfg_view(for_=Wiki, name='add_page')
-
-This indicates that the view is "for" the Wiki class and has the
-``add_page`` view_name. After injecting this decorator, we can now
-*remove* the following from our ``configure.zcml`` file:
-
-.. code-block:: xml
- :linenos:
-
- <view
- for=".models.Wiki"
- name="add_page"
- view=".views.add_page"
- />
-
-Our new decorator takes its place.
-
-The ``edit_page`` view function
--------------------------------
-
-The decorator above the ``edit_page`` function will be:
-
-.. code-block:: python
- :linenos:
-
- @bfg_view(for_=Page, name='edit_page')
-
-This indicates that the view is "for" the Page class and has the
-``edit_page`` view_name. After injecting this decorator, we can now
-*remove* the following from our ``configure.zcml`` file:
-
-.. code-block:: xml
- :linenos:
-
- <view
- for=".models.Page"
- name="edit_page"
- view=".views.edit_page"
- />
-
-Our new decorator takes its place.
-
-Adding a Scan Directive
-=======================
-
-In order for our decorators to be recognized, we must add a bit of
-boilerplate to our ``configure.zcml`` file. Add the following tag
-anywhere beneath the ``<include package="repoze.bfg.includes">`` tag
-but before the ending ``</configure>`` tag within ``configure.zcml``:
-
-.. code-block:: xml
- :linenos:
-
- <include package="repoze.bfg.includes" />
- <scan package="."/>
-
-Viewing the Result of Our Edits to ``views.py``
-===============================================
-
-The result of all of our edits to ``views.py`` will leave it looking
-like this:
-
-.. literalinclude:: src/viewdecorators/tutorial/views.py
- :linenos:
- :language: python
-
-Viewing the Results of Our Edits to ``configure.zcml``
-======================================================
-
-The result of all of our edits to ``configure.zcml`` will leave it
-looking like this:
-
-.. literalinclude:: src/viewdecorators/tutorial/configure.zcml
- :linenos:
- :language: xml
-
-Running the Tests
-=================
-
-We can run these tests by using ``setup.py test`` in the same way we
-did in :ref:`running_tests`. Assuming our shell's current working
-directory is the "tutorial" distribution directory:
-
-On UNIX:
-
-.. code-block:: bash
-
- $ ../bin/python setup.py test -q
-
-On Windows:
-
-.. code-block:: bash
-
-
- c:\bigfntut\tutorial> ..\Scripts\python setup.py test -q
-
-Hopefully nothing will have changed. The expected result looks
-something like:
-
-.. code-block:: bash
-
- .........
- ----------------------------------------------------------------------
- Ran 9 tests in 0.203s
-
- OK
-
-Viewing the Application in a Browser
-====================================
-
-Once we've set up the WSGI pipeline properly, we can finally examine
-our application in a browser. We'll make sure that we didn't break
-any views by trying each of them.
-
-- Visiting `http://localhost:6543/ <http://localhost:6543/>`_ in a
- browser invokes the ``view_wiki`` view. This always redirects to
- the ``view_page`` view of the FrontPage page object.
-
-- Visiting `http://localhost:6543/FrontPage/
- <http://localhost:6543/FrontPage/>`_ in a browser invokes the
- ``view_page`` view of the front page page object. This is because
- it's the *default view* (a view without a ``name``) for Page objects.
-
-- Visiting `http://localhost:6543/FrontPage/edit_page
- <http://localhost:6543/FrontPage/edit_page>`_ in a browser invokes
- the edit view for the front page object.
-
-- Visiting `http://localhost:6543/add_page/SomePageName
- <http://localhost:6543/add_page/SomePageName>`_ in a browser invokes
- the add view for a page.
-
-
-