diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst
index abcee42..db4ac40 100644
--- a/docs/source/configuration.rst
+++ b/docs/source/configuration.rst
@@ -143,16 +143,3 @@ app is running I can access ``foo`` values like::
>>> conf.foo
Config({'bar': True, 'baz': False})
-
-Files and Structure
-===================
-Pecan gives you a ``start.py`` file so you can manually run your application
-from the command line. By default, this file requires a parameter which is
-a configuration file without the ``.py`` extension.
-
-If you have a configuration file named ``config.py`` you would need to pass it
-to ``start.py`` like::
-
- python start.py config
-
-
diff --git a/docs/source/databases.rst b/docs/source/databases.rst
index 90f2522..5e42a44 100644
--- a/docs/source/databases.rst
+++ b/docs/source/databases.rst
@@ -1,25 +1,25 @@
.. _databases:
-Working with Databases and ORM's
+Working with Databases, Transactions, and ORM's
=============
Out of the box, Pecan provides no opinionated support for working with databases,
but it's easy to hook into your ORM of choice with minimal effort. This article
-details best practices for integration the popular Python ORM, SQLAlchemy, into
+details best practices for integrating the popular Python ORM, SQLAlchemy, into
your Pecan project.
-init_model and Preparing Your Model
+``init_model`` and Preparing Your Model
----------------
Pecan's default quickstart project includes an empty stub directory for implementing
your model as you see fit::
-.
-└── test_project
- ├── app.py
- ├── __init__.py
- ├── controllers
- ├── model
- │ ├── __init__.py
- └── templates
+ .
+ └── test_project
+ ├── app.py
+ ├── __init__.py
+ ├── controllers
+ ├── model
+ │ ├── __init__.py
+ └── templates
By default, this module contains a special method, ``init_model``::
@@ -90,10 +90,14 @@ Binding Within the Application
----------------
There are several approaches that can be taken to wrap your application's requests with calls
to appropriate model function calls. One approach is WSGI middleware. We also recommend
-Pecan hooks (see ref:`hooks`). Pecan comes with ``TransactionHook``, a hook which can
-be used to wrap requests in transactions for you. To use it, you simply include it in your
+Pecan :ref:`hooks`. Pecan comes with ``TransactionHook``, a hook which can
+be used to wrap requests in transactions for you. To use it, simply include it in your
project's ``app.py`` file and pass it a set of functions related to database binding::
+ from pecan import conf, make_app
+ from pecan.hooks import TransactionHook
+ from test_project import model
+
app = make_app(
conf.app.root,
static_root = conf.app.static_root,
@@ -111,28 +115,30 @@ project's ``app.py`` file and pass it a set of functions related to database bin
)
For the above example, on HTTP POST, PUT, and DELETE requests, ``TransactionHook`` behaves in the
-following manner::
+following manner:
+
+#. Before controller routing has been determined, ``model.start()`` is called. This function should bind to the appropriate SQLAlchemy engine and start a transaction.
-#. Before controller routing has been determined, ``model.start()`` is called. This function
-should bind to the appropriate SQLAlchemy engine and start a transaction.
#. Controller code is run and returns.
-#. If your controller or template rendering fails and raises an exception, ``model.rollback()``
-is called and the original exception is re-raised. This allows you to rollback your database
-transaction to avoid committing work when exceptions occur in your application code.
+
+#. If your controller or template rendering fails and raises an exception, ``model.rollback()`` is called and the original exception is re-raised. This allows you to rollback your database transaction to avoid committing work when exceptions occur in your application code.
+
#. If the controller returns successfully, ``model.commit()`` and ``model.clear()`` are called.
On idempotent operations (like HTTP GET and HEAD requests), TransactionHook behaves in the following
-manner::
+manner:
#. ``model.start_read_only()`` is called. This function should bind to your SQLAlchemy engine.
+
#. Controller code is run and returns.
+
#. If the controller returns successfully, ``model.clear()`` is called.
Splitting Reads and Writes
----------------
-Employing the above strategy with ``TransactionHook`` makes it very simple to split database
-reads and writes based upon HTTP methods (i.e., GET/HEAD requests are read and would potentially
+Employing the strategy above with ``TransactionHook`` makes it very simple to split database
+reads and writes based upon HTTP methods (i.e., GET/HEAD requests are read-only and would potentially
be routed to a read-only database slave, while POST/PUT/DELETE requests require writing, and
-would bind to a master database with read/write priveleges) It's also very easy extend
+would always bind to a master database with read/write privileges). It's also very easy to extend
``TransactionHook`` or write your own hook implementation for more refined control over where and
when database bindings are called.
\ No newline at end of file
diff --git a/docs/source/hooks.rst b/docs/source/hooks.rst
index 8179d9c..4a20186 100644
--- a/docs/source/hooks.rst
+++ b/docs/source/hooks.rst
@@ -9,7 +9,7 @@ There is nothing wrong with WSGI Middleware, and actually, it is really easy to
use middleware with Pecan, but it can be hard (sometimes impossible) to have
access to Pecan's internals from within middleware. Hooks make this easier.
-Hooks offer four ways of dealing with a request:
+Hooks allow you to execute code at key points throughout the life cycle of your request:
* ``on_route``: called before Pecan attempts to route a request to a controller
@@ -22,21 +22,27 @@ Hooks offer four ways of dealing with a request:
Implementation
--------------
In the below example, we will write a simple hook that will gather
-some information about the request and print it out to ``stdout``.
+some information about the request and print it to ``stdout``.
-Your hook implementation needs to import ``PecanHook`` so it can be used as a base class::
+Your hook implementation needs to import ``PecanHook`` so it can be used as a base class.
+From there, you'll need to override the ``on_route``, ``before``, ``after``, or ``on_error`` methods::
from pecan.hooks import PecanHook
class SimpleHook(PecanHook):
+ def before(self, state):
+ print "\nabout to enter the controller..."
+
def after(self, state):
print "\nmethod: \t %s" % state.request.method
print "\nresponse: \t %s" % state.response.status
-``on_route``, ``before``, and ``after`` are passed a shared state object which includes useful
+``on_route``, ``before``, and ``after`` are each passed a shared state object which includes useful
information about the request, such as the request and response object, and which controller
was chosen by Pecan's routing.
+
+``on_error`` is passed a shared state object **and** the original exception.
Attaching Hooks
--------------
@@ -63,16 +69,17 @@ using the ``__hooks__`` attribute on one or more controllers::
@expose('json')
def index(self):
+ print "DO SOMETHING!"
return dict()
-Running it
-----------
Now that our ``SimpleHook`` is included, let's see what happens when we run
-the app and browse the application::
+the app and browse the application from our web browser::
pecan serve config.py
serving on 0.0.0.0:8080 view at http://127.0.0.1:8080
+ about to enter the controller...
+ DO SOMETHING!
method: GET
response: 200 OK
diff --git a/docs/source/validation_n_errors.rst b/docs/source/validation_n_errors.rst
index 50c5faa..6df6b01 100644
--- a/docs/source/validation_n_errors.rst
+++ b/docs/source/validation_n_errors.rst
@@ -9,7 +9,7 @@ error handling activities, like:
* Transforming strings from form submissions into useful Python objects.
* Simplifying the process of re-displaying form values and associated error messages inline.
-Rather than re-inventing the wheel, Pecan uses FormEncode for schemas and form validation.
+Rather than re-inventing the wheel, Pecan uses `FormEncode