Version 0.7
===========
*This document is a work in progress*

Tipfy 0.7 is a huge step with several additional features and improvements in
tipfy's core design. Here's an overview of what is new:

- NEW: Tipfy can now be used outside of App Engine. Variables that are not
  thread safe such as Tipfy.app and Tipfy.request are now stored in thread
  locals for non-App Engine servers.

- NEW: app and request globals are back, available as globals in tipfy. They
  are stored in thread locals outside of App Engine. Current usage doesn't
  change but now it is possible to import app and request directly, like this::

      # app and request are proxies to Tipfy.app and Tipfy.request
      from tipfy import app, request

- NEW: tipfy.utils provides the functions json_decode and json_encode, which
  will use the best simplejson module available and apply correct escaping.

- NEW: added a simple template engine (from Tornado), mainly to remove
  debugger dependency from Jinja2.

- NEW: KeyProperty model was added to the db extension. [explain]

- NEW: ETagMiddleware [explain]

- REMOVED: 3 configuration keys, and added 4 properties to the Tipfy instance:
  - Tipfy.dev: True is the app is using the dev server, False otherwise.
  - Tipfy.appengine: True is the app is running on App Engine, False otherwise.
  - Tipfy.application_id: the application ID as defined in *app.yaml*.
  - Tipfy.current_version_id: the deployed version ID. Always '1' when using
    the dev server.

- NEW: auth, debugger, i18n and sessions are now part of tipfy core. They are
  all optional, lazily loaded, default and overridable implementations.


Auth
----
- NEW: Auth is now part of tipfy core. The auth store is configured in tipfy
  module and uses an App Engine store by default::

      config['tipfy'] = {
          'auth_store_class': 'tipfy.auth.appengine.AppEngineAuthStore',
      }

  Then it is available through lazy loading in both Request and RequestHandler:

  - Request.auth_store provides access to the configured auth store.
  - RequestHandler.auth is a shortcut to Request.auth_store.

  Example::

      class MyHandler(RequestHandler):
          def get(self, **kwargs):
              # Get the current user, if any.
              current_user = self.auth.user

              # Generate a login URL.
              login_url = self.auth.login_url()

- NEW: AppEngineMixedAuthStore, which allows login using App Engine Auth
  across subdomains. It mixes App Engine auth with own sessions to achieve
  this.


I18n
----
- NEW: i18n is now part of tipfy core. The i18n store is configured in
  tipfy module and uses the shipped store by default::

      config['tipfy'] = {
          'i18n_store_class': 'tipfy.i18n.I18nStore',
      }

  Then it is available through lazy loading in both Request and RequestHandler:

  - Request.i18n_store provides access to the configured i18n store.
  - RequestHandler.i18n is a shortcut to Request.i18n_store.

  Example::

      class MyHandler(RequestHandler):
          def get(self, **kwargs):
              # Get a translated string.
              message = self.i18n.gettext('Translate me')

  Global i18n functions are still available in tipfy.i18n.

- NEW: AppEngineMixedAuthStore, which allows login using App Engine Auth
  across subdomains. It mixes App Engine auth with own sessions to achieve
  this.


Debugger
--------
- NEW: Debugger is now part of tipfy core, without extra-dependencies.
  Previously the debugger was dependent on Jinja2, but now it uses the built-in
  template engine. The debugger is enabled directly in main.py as a normal
  WSGI middleware. See main.py in the project dir for a setup that also
  includes patching the dev server for better Jinja2 debugging.

- IMPROVED: the debugger now works even if the libraries are imported using
  zipimport.


Sessions
--------
- NEW: Sessions are now part of tipfy core. The auth store is configured in
  tipfy module and uses the shipped store by default::

      config['tipfy'] = {
          'session_store_class': 'tipfy.sessions.SessionStore',
      }

  Then it is available through lazy loading in both Request and RequestHandler:

  - Request.session provides access to the session using the configured
    backend and session key.
  - Request.session_store provides session related utilities such as getting
    sessions from different backends and setting/deleting cookies.
  - RequestHandler.session is a shortcut to Request.session.

  Example::

      class MyHandler(RequestHandler):
          def get(self, **kwargs):
              # Set a value in the current session.
              self.session['foo'] = 'bar'

              # Get a list of flash messages.
              flashes = self.session.get_flashes()

              # Add a flash message.
              self.session.flash('Hello, world!')

              # Get a session from a different backend.
              session = self.request.session_store.get_session(backend='memcache')

- NEW: secure cookie has a faster and easier to use implementation. See
  tipfy.sessions.SecureCookieStore.

- NEW: flash messages now have a category and configurable key [explain]


Routing
-------
- NEW: added a router object that centralizes URL matching, dispatching and
  building URLs. It makes it more flexible to implement alternative routing
  schemes. Some implications:

  - Moved methods from Tipfy that are now part of the router functionality:
    get_url_map(), add_url_rule(), match_url(), pre_dispatch(), dispatch() and
    post_dispatch(). If you extended them in some way, you must now extend
    the router.
  - URL building is centralized in the router, so url_for() methods
    (from RequestHandler or Tipfy) call router.build().

- NEW: added a new rule converter, RegexConverter, which can be usefull
  specially to match subdomains excluding some default ones (e.g., 'www').
  For example, here we define a rule for the root URL of all non-www
  subdomains, and a different one for www::

      rules = [
          Rule('/', endpoint='index', handler='IndexHandler'),
          Subdomain(r'<regex("(?!www\\b)\\w+"):namespace>', [
              Rule('/', endpoint='index', handler='SubdomainIndexHandler'),
          ]),
      ]

- NEW: rules can define the method that will be called from the handler. The
  method is defined using a 'Handler:method' notation in the Rule
  definition. Example:

      # handlers.py

      class MyHandler(RequestHandler):
          def my_method(self, **kwargs):
              return Response('I am not coming from a get() method!')

      # main.py

      app = Tipfy(rules=[
          Rule('/', name='home', handler='handlers.MyHandler:my_method')
      ])

  In this example, the request method is ignored since the Rule defines which
  handler method will handle all requests.

- IMPROVED: url_for() has a few more keywords with special meanings, all
  prefixed with underscore. This is the full list of special keywords:

  - **_full**: If True, builds an absolute URL.
  - **_method**: Uses a rule defined to handle specific request
    methods, if any are defined.
  - **_scheme**: URL scheme, e.g., `http` or `https`. If defined,
    an absolute URL is always returned.
  - **_netloc**: Network location, e.g., `www.google.com`. If
    defined, an absolute URL is always returned.
  - **_anchor**: If set, appends an anchor to generated URL.

- IMPROVED: Rule now accepts 'name' as keyword argument. It is an alias to
  'endpoint'. This is just for semantic correctness in tipfy context and
  'endpoint' will still work::

      Rule('/', name='home', handler='handlers.HomeHandler')
      # is the same as...
      Rule('/', endpoint='home', handler='handlers.HomeHandler')

- CHANGED: URL rules are not automatically loaded from urls.py. Instead tipfy
  is instantiated passing the URL rules, like webapp. Later other rules can
  be added to the router. For example:

      app = Tipfy(rules=[
          Rule('/', name='home', handler='handlers.HomeHandler'),
          Rule('/about', name='about', handler='handlers.AboutHandler'),
      ])

      # Add an extra rule.
      app.router.add(Rule('/contact', name='contact', handler='handlers.ContactHandler'))

      # Or add a list of rules.
      app.router.add([
          Rule('/products', name='products', handler='handlers.ProductsHandler'),
          Rule('/history', name='history', handler='handlers.HistorytHandler'),
      ])

  The previous behavior using urls.py is easy to reproduce. For example,
  given a urls.py like the following:

      from tipfy import Rule

      def get_rules(app):
          rules = [
              Rule('/', name='home', handler='handlers.HomeHandler'),
              Rule('/about', name='about', handler='handlers.AboutHandler'),
              Rule('/contact', name='contact', handler='handlers.ContactHandler'),
          ]

          return rules

  You can still import and use it in main.py:

      from urls import get_rules

      app = Tipfy()

      # Add a list of rules.
      app.router.add(get_rules(app))

  Or you can simply have a list of rules defined in urls.py, outside of a
  function, and import it:

      from urls import rules

      app = Tipfy(rules=rules)

- IMPROVED: redirect() (or self.redirect() in handlers) now accepts relative
  URLs. If a relative URL is passed, it'll be joined to the current request
  URL. This is mostly useful to redirect to the result of url_for() without
  needing to build a full URL::

      # current URL is http://localhost/foo/bar

      # redirects to http://localhost/baz
      return redirect('/baz')

  ...but it also allows curious results for relative paths:

      # current URL is http://localhost/foo/bar

      # redirects to http://localhost/foo/baz
      return redirect('./baz')

      # redirects to http://localhost/baz
      return redirect('../baz')


Config
------
- IMPROVED: configuration now behaves exactly like a dictionary, still
  auto-loading configuration values when needed and honoring required configs.
  For example, we always used this::

      secret_key = self.app.get_config('tipfy.sessions', 'secret_key')

  Now it is also possible to use direct access and dict methods::

      secret_key = self.app.config['tipfy.sessions']['secret_key']
      # or...
      secret_key = self.app.config['tipfy.sessions'].get('secret_key')
      # or...
      secret_key = self.app.config.get('tipfy.sessions').get('secret_key')

  The previous get_config() method works as always.


WSGI App
--------
- REMOVED: make_wsgi_app() and run_wsgi_app(). [explain]

- NEW: Added Tipfy.make_response(). With this, handler methods are not
  required to always return a response. If a string or unicode is returned,
  it is converted to a response. Other possibility is to return a tuple to
  build a response. HTTPExceptions are also converted to a proper response
  when returned. Example:

      class MyHandler(RequestHandler):
          def get(self, **kwargs):
              # Returns a simple string.
              return 'Hello, World'

              # Returns a tuple of values to create a response.
              return 'Not Found', 404

              # Return an HTTPException which will be converted to a response.
              return MethodNotAllowed()

- NEW: app has now a debug attribute, set when it is initialized with
  debug=True.

- IMPROVED: Exception handling [explain]


Request
-------
- NEW: Request can now automatically decode JSON requests. If the request
  mimetype is 'application/json', the decoded request data is available in
  request.json.


Buildout
--------
- IMPROVED: simplified buildout project structure to remove cruft:
  - 'etc/' is removed and its contents go to 'var/'.
  - 'distlib/' is now placed in 'lib/dist/'.

- ...


Version 0.6.3 - August 24, 2010
===============================
- Updated zc.buildout 1.5.0 was causing problems, so now we force 1.4.4 in
  bootstrap.py.


Version 0.6.2 - August 20, 2010
===============================
- Added Tipfy.run(): run_wsgi_app(app) is now deprecated and calls app.run().


Version 0.6.1 - August 19, 2010
===============================
- RequestHandler instances are now callables. __call__() is used instead of
  dispatch(), which is now deprecated and just calls the instance.

- Added RequestHandler methods:
  - abort(), a wrapper for abort().
  - get_config(), a wrapper for Config.get_or_load().
  - handle_exception()
  - redirect().
  - redirect_to(): redirect() + url_for()
  - url_for():

- Added get_valid_methods(), which returns the HTTP methods a handler can
  handle. This is used when raising MethodNotAllowed, which now correctly
  lists the allowed methods as required by HTTP 1.1 spec.

- Added HandlerPrefix, a rule factory to prefix handler definition in rules.
  For example, take these rules::

      rules = [
          HandlerPrefix('my_app.handlers.', [
              Rule('/', endpoint='index', handler='IndexHandler'),
              Rule('/entry/<entry_slug>', endpoint='show', handler='ShowHandler'),
          ]),
      ]

  These are the same as::

      rules = [
          Rule('/', endpoint='index', handler='my_app.handlers.IndexHandler'),
          Rule('/entry/<entry_slug>', endpoint='show', handler='my_app.handlers.ShowHandler'),
      ]

- If the requested method is not in ALLOWED_METHODS, a NotImplemented exception
  ("501 Not Implemented") is raised, instead of MethodNotAllowed ("405 Method
  Not Allowed").

- ALLOWED_METHODS are now listed in upper case.

- Moved the logic from Tipfy.get_config() to the new method
  Config.get_or_load().

- REMOVED: app_id argument from Tipfy.__init__(). Not really needed or used.

- Exceptions caused when importing a badly configured middleware class are now
  caught and a warning is emitted.


Version 0.6 - July 25, 2010
===========================
- Handler definitions in URL rules can now also be a RequestHandler class,
  instead of a string.

- Added support for single-file apps, much like we see in webapp. Tipfy can
  now be instantiated passing a list of URL rules, so apps can be entirely
  defined in a single file mapped in app.yaml, without the need for a separate
  urls.py::

      import sys
      sys.path.insert(0, 'distlib')

      from tipfy import RequestHandler, Rule, make_wsgi_app, run_wsgi_app

      class HelloWorldHandler(RequestHandler):
          def get(self, **kwargs):
              return 'Hello, World!'

      class ByeWorldHandler(RequestHandler):
          def get(self, **kwargs):
              return 'Bye, World!'

      app = make_wsgi_app(rules=[
          Rule('/', endpoint='hello', handler=HelloWorldHandler),
          Rule('/bye', endpoint='bye', handler=ByeWorldHandler),
      ])

      def main():
          run_wsgi_app(app)

      if __name__ == '__main__':
          main()

  This also helps to test handlers defined in a test file::

      import unittest

      from tipfy import RequestHandler, Rule, Tipfy, redirect

      class TestHandler(unittest.TestCase):
          def test_redirect(self):
              class Handler1(RequestHandler):
                  def get(self, **kwargs):
                      return redirect(self.request.url_for('test2'))

              class Handler2(RequestHandler):
                  def get(self, **kwargs):
                      return 'Hello, World!'

              rules = [
                  Rule('/1', endpoint='test1', handler=Handler1),
                  Rule('/2', endpoint='test2', handler=Handler2),
              ]

              app = Tipfy(rules=rules)
              client = app.get_test_client()
              response = client.get('/1', follow_redirects=True)

              assert response.data == 'Hello, World!'

- Added get_url_map() and add_url_rule() methods to the WSGI app.

- The returned value from a RequestHandler is converted to a proper Response
  object if needed. So a simple string can also be returned, among other
  options::

      from tipfy import RequestHandler, Response

      class HelloWorldHandler(RequestHandler):
          def get(self):
              return 'Hello, World!'


Backwards compatibility warnings
--------------------------------
- REMOVED: 'url_rules', 'url_map' and 'url_map_kwargs' config keys, now
  redundant with the possibility to override get_url_map() or instantiate
  the WSGI app passing URL rules.

- REMOVED: local, request and app. Replacements for these are:
  * request, local.request: Tipfy.request, a class attribute in the WSGI app.
  * app, local.app: Tipfy.app, a class attribute in the WSGI app.
  * local: request.context and request.registry, dictionaries set for a
    request.

- REMOVED: old aliases: WSGIApplication (Tipfy) and REQUIRED_CONFIG
  (REQUIRED_VALUE)


Version 0.5.9 - July 1st, 2010
==============================
- Small improvement in get_config(): previously when you called
  get_config without passing a key value, and the module was partially
  configured by the user, it would return only the partial configuration.
  Now if the module defaults weren't previously loaded, it will always be
  loaded and set as default in config. This way, get_config('module') will
  return the full dictionary of defaults for 'module', plus user configured
  values.

- Added argument 'loaded' to Config constructor, to set the initially loaded
  modules.

- Updated SDK URL to 1.3.5 in buildout.cfg.


Version 0.5.8 - June 30, 2010
=============================
- Added request.registry, as we slowly move away from thread-local variables.


Version 0.5.7 - June 27, 2010
=============================
- REMOVED: normalize_callable(). Unused.

- Renamed Tipfy.context to Tipfy.registry, as the purpose is to serve as a
  registry for instances reused by the app, like Jinja2 or Genshi environments.


Version 0.5.6 - June 22, 2010
=============================
- REMOVED: RegexConverter. Unused and undocumented.

- REMOVED: wsgi_app_id config key. Unused.

- Middleware lists can now also have middleware instances. This allows
  different instances of same middleware classes to be used with different
  configuration:

  my_middleware = MyMiddleware(foo='bar')

  class MyRequestHandler(RequestHandler):
      middleware = [my_middleware]

      # ...

  my_middleware2 = MyMiddleware(foo='zing')

  class MyRequestHandler2(RequestHandler):
      middleware = [my_middleware2]

      # ...

- Fixed using tipfy with zipimport. For whatever reason py_zipimport fails to
  load werkzeug modules if werkzeug package is not imported first. This is
  probably related to how werkzeug lazy-loads modules -- an edge case not
  covered by py_zimpimport.


Version 0.5.5 - June 2nd, 2010
==============================
- All .ext packages were moved to separate releases. Tipfy is again a single
  file with a single dependency (Werkzeug), as it was in the beginning.


Version 0.5.4 - May 27, 2010
============================
Bug fix release following 0.5.3.

- REMOVED 'pre_run_app' hook. Use post_make_app instead:

    def post_make_app(self, app):
        # Wrap the callable, so we keep a reference to the app...
        app.wsgi_app = my_middleware(app.wsgi_app)
        # ...and return the original app.
        return app

- IMPROVED tipfy.ext.appstatts and tipfy.ext.debugger: middleware is set on
  'post_make_app'.


Version 0.5.3 - May 27, 2010
============================

Backwards compatibility warnings
--------------------------------

- ADDED: RequestHandler.__init__(app, request), setting references to current
  WSGIApplication and Request in the handler. If you implemented __init__() on
  RequestHandler, please update it to accept these arguments:

    class MyRequestHandler(RequestHandler):
        def __init__(self, app, request):
            super(MyRequestHandler, self).__init__(app, request)
            ...

  We now have access to the URL rule and rule arguments that matched on
  __init__(), and can read app configuration without relying on local.

- CHANGED: the signature of RequestHandler.dispatch(), as a consequence of
  adding __init__() to the handler class. Request method and rule arguments
  are taken from the request set on __init__().

- REMOVED: tipfy.ext.session.MessagesMixin.set_form_error(). It required
  i18n, which requires Babel. Sessions should not depend on i18n. Now they
  don't. Please implement it on a separate mixin or base handler if you used
  it.

- We're now slowly getting rid of globals and local is the first target, as it
  doesn't make sense on App Engine.

  While local.request and local.app still works, you should start using
  Tipfy.request and Tipfy.app if globals are required in helper functions (they
  are now available in the handler, which should cover most use cases).

  Tipfy.request and Tipfy.app are set and cleaned on each request. It is
  fast and perfectly valid on App Engine, but unfortunately this is not a
  good idea outside of App Engine where you need to take care of threading.

  So an application using Tipfy outside of App Engine needs to setup a proxy on
  these attributes pointing to a thread local. This monkeypatch covers it all:

    from werkzeug import Local

    local = Local()

    def set_wsgi_app(self):
        local.app = self

    def set_request(self, request):
        local.request = request

    def cleanup(self):
        local.__release_local__()

    Tipfy.set_wsgi_app = set_wsgi_app
    Tipfy.set_request = set_request
    Tipfy.cleanup = cleanup
    Tipfy.app = local('app')
    Tipfy.request = local('request')

  This makes Tipfy.app and Tipfy.request thread-safe outside of App Engine.

tipfy.routing
-------------

- CHANGED: Added _anchor keyword argument to url_for() and redirect_to(), to
  append an anchor to the generated URL. Also keyword arguments in these two
  functions are now prefixed with a '_':

    url_for(endpoint, _full=False, _method=None, _anchor=None, **kwargs)
    redirect_to(endpoint, _method=None, _anchor=None, _code=302, **kwargs)

  This is just a sanity measure to avoid collisions with variables set in
  rules. The old, non-prefixed keywords are still accepted, but they should
  be removed on 1.0.

- ADDED: request.url_for(). tipfy.url_for() just calls request.url_for().

tipfy.ext.i18n
--------------

- ADDED: functions to ext.i18n: get_timezone_name(), parse_date(),
  parse_datetime() and parse_time().

- IMPROVED: All datetime and number format functions in ext.i18n now accept a
  locale argument. If not set, uses the currently loaded locale (as before).

- ADDED: babel.cfg to the project directory, with configuration to extract
  translation messages using pybabel. Also added locale directory to the app
  dir.

tipfy.application
-----------------

- ADDED: a Request object that extends werkzeug's Request. This is backwards
  compatible and stores url_adapter, matched rule and rule arguments.
  app.url_adapter, app.rule and app.rule_args are still set, but they will be
  removed on 1.0.

- ADDED: app.dev attribute, as a shortcut to the development flag set in
  config.

- ADDED: app.get_config(). tipfy.get_config() just calls app.get_config().

- FIXED: logging debug level is set correctly now when in development.

- ADDED some ideas from Flask:
  - Local variables are not cleaned when in development and an exception is
    raised. This allows the debugger to still access request and other
    variables from local in the interactive shell.
  - Added a wsgi_app() function to WSGIApplication, wrapping __call__().
  - Added a make_response() method to WSGIApplication, so that handlers
    can return not only a Response object.
  - Refactoring: split pre_dispatch/dispatch/post_dispatch into separate
    functions. Makes it easier to override these methods in a middleware.
  - Added function get_test_client() to return a werkzeug.Client for the wsgi
    app.
  - Added function ext.jinja2.get_template_attribute().
  - ADDED: a Response object that extends werkzeug's Response, with default
    mimetype set to 'text/html'.

- REMOVED:
  - tipfy.response and 'extensions' configuration key. They were deprecated
    since 0.5 and not useful at all since then.
  - tipfy.local_manager. No longer needed since latest Werkzeug.


Miscelaneous
------------

- FIXED: XMPP handlers. Now fully tested.

- IMPROVED: render_json_response() now accepts all arguments and keyword
  arguments accepted by simplejson.dumps().

- ADDED: tipfy.ext.mail, a simple RequestHandler for inbound mail.


Version 0.5.2 - May 06, 2010
============================
- Added a buildout setup to manage external dependencies. It is now
  the preferred method for setting and updating tipfy and app libraries.
  A full build will still be provided, though.

- Experimental support for loading all libraries using zipimport. The buildout
  setup is capable of generating the zip and it is included in sys.path by
  default. (issue #12)

- Tipfy can now be installed using easy_install.

- Fixed bug in ext.taskqueue.Mapper that would prevent changes from being
  written to the datastore. (Kaelten)

- Fixed debugger interactive console which was not working since Werkzeug 0.6.
  (issue #11)

- Fixed ext.blobstore, broken since SDK 1.3.3 which made some previously
  public blobstore functions protected.

- REMOVED: tipfy.application.PatchedCGIHandler. The CGIHandler bug was fixed in
  the 1.3.2 SDK.


Version 0.5.1 - April 18, 2010
==============================
- Upgraded Werkzeug to 0.6.1.

- Upgraded Jinja2 to 2.4.

- Fixed bug in ext.auth.is_current_user_admin(). Thanks, danil.

- Added get_locale() to tipfy.ext.i18n. Internally it now uses get_locale()
  instead of local.locale directly, so that locales are autoloaded when not set.


Version 0.5 - Codename Spiff - April 10, 2010
=============================================
- Minor bugs fixed in the released candidate, some more docs added, and we are
  good to go.


Version 0.5rc - April 03, 2010
==============================
- This is a major release with several backwards compatibility breaks. API
  and architecture consistency was one of the major goals -- better to break
  things sooner than later.

- Added a middleware system for handlers: handlers can now hook pre, post and
  handle exception routines in the handler class itself. Very useful to make
  base handlers with similar requirements and characteristics, without adding
  overhead to non-related, simpler handlers. A few initial middleware classes
  were added:

  - AppstatsMiddleware
  - AuthMiddleware
  - DebuggerMiddleware
  - I18nMiddleware
  - SessionMiddleware

  This deprecates several functionalities performed by app hooks before.
  While hooks are still useful to wrap and extend WSGIApplication, middleware
  applied directly to handlers are more appropriate in several use cases.

  Thanks to Thomas Johansson (prencher) for the invaluable feedback on this.

- The previous 'extensions' system for the WSGI app now uses the same middleware
  system used by the handlers. You can register middleware for the WSGI app and
  these classes can also act as handler middleware.

- Added tipfy.ext.appstats, a middleware to use the appstats profiling tool.

- Added tipfy.ext.blobstore, with handler mixin classes to handle blobstore
  upload and serving.

- Added tipfy.ext.xmpp, with base handlers for XMPP bots.

- Added several other mixin classes. Each of them add a couple of attributes
  or methods to a request handler. The full list of mixins:

  - AclMixin
  - BlobstoreDownloadMixin
  - BlobstoreUploadMixin
  - Jinja2Mixin
  - MakoMixin
  - MessagesMixin
  - SessionMixin

- Added some new db.Model properties to tipfy.ext.db:

  - JsonProperty
  - TimezoneProperty (thanks to Thomas Johansson for this one)

- Added post_make_app application hook.

- tipfy.ext.session was rewritten from scratch. It now works as a provider of
  sessions and related stuff, such as signed flash messages and secure cookies.

- tipfy.ext.messages was removed and the existing functionality (flash messages
  and messages container) was merged into tipfy.ext.session.

- tipfy.ext.user was renamed to tipfy.ext.auth.

- tipfy.ext.i18n doesn't require initialization anymore; it is initialized when
  first used. Locale for the current request can be automatically loaded using
  several configurable methods (taking arguments from GET, POST, cookies or
  URL parameters).

- Added number formatting functions to tipfy.ext.i18n: format_number,
  format_decimal, format_currency, format_percent, format_scientific,
  parse_number, parse_decimal.

- URL rules are no longer stored in memcache. This can be done in urls.py if
  one wants it. Tipfy core won't make *any* API calls anymore.

- Added a directory for examples in Tipfy's repository, compiling all examples
  from tutorials and some new ones.

- Several improvements everywhere, and a lot more documentation and unit test
  coverage.
