TODO
----
- Use router.dispatch() on WSGIApplication.handle_exception(), so that exception
  handlers can also be lazily loaded.

- Docs:
  - WSGIApplication
  - URL routing
  - RequestHandler
  - Request
  - Response
  - Exception handling
  - Configuration system
  - Unit testing


DONE
----
- 100% test coverage
- Use webapp.Request (extended webob.Request)
- Use webob.Response
- HTTP exceptions
- Simple exception handling
- Configuration system
- Lazy RequestHandlers (optional)
- RequestHandler methods can receive keyword arguments
- RedirectHandler (automatic redirect from old urls)
- Reversible URLs:
  - checks for missing keywords when building urls
  - checks for keyword validity when building urls
  - appends extra keywords as arguments:

    >>> route = Route('/blog/{foo}', 'my_handler')
    >>> url = route.build(foo='bar', baz='ding')
    >>> /blog/bar?baz=ding

- redirect_to(): redirects to a built URL.
- Nick Johnson's ideas:
  - use RequestHandler.__call__() instead of RequestHandler.dispatch().
  - allow positional arguments passed to RequestHandler methods for better
    compatibility with webapp.
- Easy unit testing::

      request = Request.blank('/')
      request.get_response(WSGIApplication())

      # or...

      from webtest import TestApp

      test_app = TestApp(WSGIApplication())
      response = test_app.get('/')


REMOVED
-------
- RequestHandler plugins. Following Nick Johnson's suggestion, leave it up
  to the implementation. A mixin can override ``__call__`` to implement hooks.
  Original implementation looked like this::

    class RequestHandler(object):
        """Base HTTP request handler. Clients should subclass this class.

        Subclasses should override get(), post(), head(), options(), etc to handle
        different HTTP methods.

        Implements most of ``webapp.RequestHandler`` interface.
        """
        #: A list of plugin instances. A plugin can implement two methods that
        #: are called before and after the current request method is executed,
        #: except if the chain is stopped.
        #:
        #: before_dispatch(handler)
        #:     Called before the requested method is executed. If returns False,
        #:     stops the plugin chain do not execute the requested method.
        #:
        #: after_dispatch(handler)
        #:     Called after the requested method is executed. If returns False,
        #:     stops the plugin chain. These are called in reverse order.
        plugins = []

        def __call__(self, _method_name, *args, **kwargs):
            """Dispatches the requested method. If plugins are set, executes
            ``before_dispatch()`` and ``after_dispatch()`` plugin hooks.

            :param _method_name:
                The method to be dispatched: the request method in lower case
                (e.g., 'get', 'post', 'head', 'put' etc).
            :param kwargs:
                Keyword arguments to be passed to the method, coming from the
                matched :class:`Route`.
            :returns:
                None.
            """
            method = getattr(self, _method_name, None)
            if method is None:
                # 405 Method Not Allowed.
                # TODO: The response MUST include an Allow header containing a
                # list of valid methods for the requested resource.
                # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.6
                return self.error(405)

            if not self.plugins:
                # No plugins are set: just execute the method.
                return method(*args, **kwargs)

            # Execute before_dispatch plugins.
            for plugin in self.plugins:
                hook = getattr(plugin, 'before_dispatch', None)
                if hook:
                    rv = hook(self)
                    if rv is False:
                        break
            else:
                # Execute the requested method.
                method(*args, **kwargs)

            # Execute after_dispatch plugins.
            for plugin in reversed(self.plugins):
                hook = getattr(plugin, 'after_dispatch', None)
                if hook:
                    rv = hook(self)
                    if rv is False:
                        break
