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 for the environment and apply correct
  escaping.

- NEW: added a simple template engine (from Tornado). This eliminates
  dependency on Jinja2 for the debugger.

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

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

- 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 zipped using
  buildout.

- NEW: Sessions is now part of tipfy core. It is available in the request
  object and lazily loaded when accessed. Request has now 'session' and
  'session_store' attributes:

  - request.session is a shortcut to get a 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.

  Examples::

      # Set a value in the current session.
      self.request.session['foo'] = 'bar'

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

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

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

- NEW: Auth is now part of tipfy core. [explain]

- 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')

- 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: 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.

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

- 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')

- NEW: added a router object that centralizes URL matching, dispatching and
  building. [explain]

- 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)

- NEW: added a new rule converter, RegexConverter. [explain]

- ...
