Skip to content

Commit

Permalink
Finish version 2 documentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
mikeorr committed Feb 13, 2012
1 parent 85034d7 commit 1856b11
Show file tree
Hide file tree
Showing 13 changed files with 206 additions and 1,358 deletions.
24 changes: 14 additions & 10 deletions CHANGES.txt
@@ -1,15 +1,19 @@
2.0 (unreleased)
2.0 (2012-02-12)
------------------
- Move repository to Github (from Butbucket); convert to Git (from Mercurial).
Rename all "v" tags, removing the prefix (v1.0.1 -> 1.0.1), so that they sort
in Git before the "pyramid_sqla" tags, and to follow Pyramid's precedent.

- Demo program in 'akhet.demo'. (Run as "python -m akhet.demo".)
- Delete 'akhet' scaffold.
- Move repository to https://github.com/Pylons/akhet and convert to Git format
(previously http://bitbucket.com/sluggo/Akhet in Mercurial format).
- Rename all "v" tags, removing the prefix (v1.0.1 -> 1.0.1), so that they sort
in Git before the older "pyramid_sqla" tags, and to follow Pyramid's precedent.

- New Akhet demo program distributed separately at
https://github.com/mikeorr/akhet_demo . It does not include a SQLAlchemy
model, thus completing the break from Akhet's origin in the former "pyramid_sqla".
- Delete 'akhet' application scaffold; the demo replaces it.
- We have a pony. (akhet.pony, based on paste.pony)
- Move non-Akhet-specific parts of the manual to the Pyramid Cookbook (as the
"Pyramid for Pylons Users" guide).
- Include for ``add_static_route`` is now "akhet.static" instead of "akhet".
- Move non-Akhet-specific parts of the manual to the Pyramid Cookbook, as the
"Pyramid for Pylons Users" guide.
- The include enabling static routes is now "akhet.static" instead of "akhet".
A backward compatibility shim exists.
- The URL generator's ``route`` method can generate either an absolute
(qualified) URL or a path-only (unqualified) URL, overriding the instance's
default mode.
Expand Down
2 changes: 1 addition & 1 deletion docs/changes.rst
@@ -1,4 +1,4 @@
Full Changelog
Full changelog
%%%%%%%%%%%%%%

.. include:: ../CHANGES.txt
55 changes: 34 additions & 21 deletions docs/demo/content.rst
@@ -1,7 +1,7 @@
Templates and stylesheets
=========================

The demo's Mako templates and stylesheets are designed to function
The demo's templates and stylesheets are designed to function
in a variety of environments, so you can copy them to your application as a starting
point. The following files are included:

Expand All @@ -27,7 +27,10 @@ first three lines are Mako constructs:
Line 1 makes the template inherit from the site template, which will add the
site's header and footer. Lines 2 and 3 are Mako methods. They output the body
title and the head title respectively.
title (the <h1> at the top of the page) and the head title (the <title> tag)
respectively. Mako templates and methods are not literally Python classes and
methods -- they compile to modules and functions respectively -- but Mako
treats them in a way that's similar to classes and methods.

The "${varname}" syntax is a placeholder which will output the named variable.
Template variables can come from several sources: (1) keys in the view's return
Expand All @@ -46,21 +49,28 @@ placeholders to plug in content from the page template. The most important
placeholder here is "${self.body()}", which outputs the body of the
highest-level template in the inheritance chain.

The template also calls "self.title()" and "self.ht_title()", and defines
default implementations for these methods. The default body title is blank; the
default head title is whatever the body title returns. So you can just set
"title" in your pages and forget about "ht_title" if you want. Sometimes you'll
have to make them different, however: (1) The head title can't contain HTML
tags like <em> -- it will display them literally rather than changing the font.
(2) Sometimes the body title is too wordy for the head title. (3) Many sites
want the site name in the head title. A general rule of thumb for the head
title is something like "Page Title &mdash; Site Name". Search engines rank the
head title highly, so it should contain all the essential words that describe
the page, and it should be less than sixty or so characters long so it fits on
one line.

There's one more method in the site template, "head_extra". It also is blank by
default, but page templates can override it to add additional tags in the head.
Note the difference between calling "${body()}" and "${self.body()}". The
former calls a <%def> method defined in the same template. The latter calls the
highest-level <%def> method with that name in the inheritance chain, which may
be in a different template.

The site template also calls "self.title()" and "self.ht_title()", and defines
default implementations for these methods. The default body title outputs
nothing (resulting in an empty title); the default head title is whatever the
body title returns. So you can just define a "title" in your pages and forget about
"ht_title" if it's the same. But there are times when you'll want to make them
different:

* When the body title contains embedded HTML tags like <em>. The head title
can't contain these because it will display them literally rather than
changing the font.
* Sometimes the body title is too wordy for the head title.
* Many sites want the site's name in the head title. A general rule of thumb is
"Short Page Title &emdash; Site Name". Or if you're part of a large
organization: "Short Page Title | Site Name | Organization Name". Search
engines pay special attention to the head title, so it should contain all the
essential words that describe the page, and it should be less than sixty or
so characters so it can be displayed in a variety of contexts.

The other kind of placeholder in the site template is "${url.app}", which is
used to form static URLs like "${url.app}/stylesheets.default.css". "url" is
Expand All @@ -70,10 +80,10 @@ top-level application mounted at "/". But if the application is mounted at a
sub-URL like "/site1", that will be what "url.app" is set to.

Normally you'd generate URLs by route name, such as "${url('home')}" or its
full form "${url.route('home')}". But static URLs don't have a route name. If
we were using Pyramid's static view there would be another way to generate
them, but the demo uses the static route so it can't do that. So we're left
with literal URLs relative to the application prefix.
full form "${url.route('home')}". But static URLs don't have a route name, and
the URL generator does not have a ``static`` method (although you can define
one in a subclass). So we're left with literal URLs relative to the application
prefix.

The template displays flash messages, which a view may have pushed into the
session before redirecting. The code for this is:
Expand Down Expand Up @@ -110,6 +120,9 @@ If you want something with more bells and whistles, some Pyramid developers
recommend `HTML5 Boilerplate`_.
It's also based on Meyer's stylesheet.

We're exploring stylesheet compilers like Less, but this version of the demo
does not include one.

.. _HTML5 Boilerplate: http://html5boilerplate.com/

Default stylesheet
Expand Down
172 changes: 50 additions & 122 deletions docs/demo/details.rst
Expand Up @@ -7,41 +7,24 @@ development.ini
The config file contains the following settings which aren't in Pyramid's
built-in scaffolds:

.. code-block:: ini
mako.directories = akhet_demo:templates
# Beaker cache
cache.regions = default_term, second, short_term, long_term
cache.type = memory
cache.second.expire = 1
cache.short_term.expire = 60
cache.default_term.expire = 300
cache.long_term.expire = 3600
# Beaker sessions
#session.type = file
#session.data_dir = %(here)s/data/sessions/data
#session.lock_dir = %(here)s/data/sessions/lock
session.type = memory
session.key = akhet_demo
session.secret = 0cb243f53ad865a0f70099c0414ffe9cfcfe03ac
The "mako.includes" setting is necessary to set Mako's search path. You can add
other Mako options here if you wish.

The "cache." settings initialize Beaker caching. This is not actually necessary
because the demo never uses a cache, but it's here for demonstration.

The "session." settings initialize Beaker sessions. This is necessary if you
use sessions or flash messages. Beaker supports several forms of session
persistence: in-memory, files, memcached, database, etc. This configuration
uses memory mode, which holds the sessions in memory until the application
quits; it obviously works only with multi-threaded servers and not with than
multi-process serviers. The default Pylons mode is file-based sessions, which
is commented here. Recent recommendations suggest memcached is the most robust
mode because it can scale to multiple servers; you can set that option if you
wish.
* mako.directories: necessary when using Mako, to set the template search
path. (Theoretically you don't need this if you specify renderers by asset
spec rather than by relative path, but I couldn't get that to work.)
* cache.\*: Beaker cache settings. These are not actually necessary because
the demo never uses a cache, but they're here for demonstration.
* session.\*: Beaker session settings. These are necessary if you use sessions
or flash messages.

Beaker supports several kinds of session
persistence: in-memory, files, memcached, database, etc. The demo's
configuration uses memory mode, which holds the sessions in memory until the application
quits. It contains commented settings for file-based sessions, which is Pylons'
default. Experienced developers seem to be choosing memcached mode nowadays.
Memory sessions disappear when the server is restarted, and work only with
multithreaded servers, not multiprocess servers. File-based sessions are
persistent, but add the complications of a directory and permissions and
maintenance. Memcached avoids all these problems, and it also scales to
multiple parallel servers, which can all share a memcached session.

If you copy the session configuration to your application, do change
"session.secret" to a random string. This is used to help ensure the integrity
Expand All @@ -51,70 +34,39 @@ of the session, to prevent people from hijacking it.
Init module and main function
=============================

Almost all of the *akhet_demo/__init__.py* module is unique to the demo, so
we'll just show the whole thing here:
The main function, in addition to the minimal Pyramid configuration, activates
Beaker sessions and caching, and sets up templates, subscribers, routes, and a
static route. The Beaker setup passes the ``settings`` dict to Beaker; that's
how your settings are read. Pyramid cofigures Mako the same way behind the
scenes, passing the settings to it. The "add_renderer" line tells Pyramid to
recognize filenames ending in ".html" as Mako templates. The subscribers
include we'll see in a minute.

.. code-block:: python
:linenos:
from pyramid.config import Configurator
import pyramid_beaker
def main(global_config, XXsettings):
""" This function returns a Pyramid WSGI application.
"""
config = Configurator(settings=settings)
# Configure Beaker sessions and caching
session_factory = pyramid_beaker.session_factory_from_settings(settings)
config.set_session_factory(session_factory)
pyramid_beaker.set_cache_regions_from_settings(settings)
# Configure renderers and event subscribers.
config.add_renderer(".html", "pyramid.mako_templating.renderer_factory")
config.include(".subscribers")
config.include("akhet.static")
# Add routes and views.
config.add_route("home", "/")
config.include("akhet.pony")
config.add_static_route("akhet_demo", "static", cache_max_age=3600)
config.scan()
return config.make_wsgi_app()
(Note: "\*\*settings" is shown as "XXsettings" because vim's syntax
highlighting gets into a fit otherwise and mishighlights up the doc source file.)

As you see, it activates Beaker sessions and caching, and sets up templates,
subscribers, routes, and a static route. The Beaker setup passes the
``settings`` dict to Beaker; that's how your settings are read. Pyramid
cofigures Mako the same way behind the scenes, passing the settings to it.
The "add_renderer" line tells Pyramid to recognize filenames ending in ".html"
as Mako templates. The subscribers include we'll see in a minute.

The static route has an include line and an "add_static_route" call.
Activating static routes involves an include line and a "config.add_static_route"
call.


Helpers
=======

*akhet_demo/lib/helpers.py* is unique to the demo. It's a Pylons-like helpers
module where you can put utility functions for your templates. The minimal
WebHelpers imports for HTML tag helpers are there, but commented. I'm tempted
to actually use the tag helpers in the site template but haven't done so yet.
The demo provides a Pylons-like helpers module,
*akhet_demo/lib/helpers.py*. You can put utility functions here for use in
your templates. The helper contains imports for WebHelper's HTML tag helpers,
but they're commented out. (WebHelpers is a Python package containing generic
functions for use in web applications and other applications.) I'm tempted to
actually use the tag helpers in the site template but haven't done so yet.

Most of WebHelpers works with Pyramid, including the popular
``webhelpers.html`` subpackage, ``webhelpers.text``, and ``webhelpers.number``.
Pyramid does not depend on WebHelpers so you'll have to add the dependency to
your application if you want to use it. The only part that doesn't work with
Pyramid is the ``webhelpers.pylonslib`` subpackage, which depends on Pylons'
special globals.
You'll have to add a WebHelpers dependency to your application if you want to
use it. The only part of WebHelpers that doesn't work with Pyramid is the
``webhelpers.pylonslib`` subpackage, which depends on Pylons' special globals.

WebHelpers 1.3 has some new URL generator classes to make it easier to use
with Pyramid. See the ``webhelpers.paginate`` documentation for details. (Note:
this is *not* the same as Akhet's URL generator; it's a different kind of class
specifically for the paginator's needs.)
Note that ``webhelpers.paginate`` requires a slightly different configuration
with Pyramid than with Pylons, because ``pylons.url`` is not available. You'll
have to supply a URL generator, perhaps using one of the convenience classes
included in WebHelpers 1.3. Paginate's URL generator is *not* Akhet's URL
generator: it's a different kind of class specific to the paginator's needs.


Subscribers
Expand Down Expand Up @@ -157,39 +109,15 @@ The views module has a base class called ``Handler`` (but it's not related to
"pyramid_handlers"). The index view demonstrates logging, optionally sets a
flash message, and invokes a Mako template renderer.

::

import logging

from pyramid.view import view_config

log = logging.getLogger(__name__)

class Handler(object):
def __init__(self, request):
self.request = request

class Main(Handler):

@view_config(route_name="home", renderer="index.html")
def index(self):
# Do some logging.
log.debug("testing logging; entered Main.index()")

# Push a flash message if query param 'flash' is non-empty.
if self.request.params.get("flash"):
import random
num = random.randint(0, 999999)
message = "Random number of the day is: %s." % num
self.request.session.flash(message)
# Normally you'd redirect at this point but we have nothing to
# redirect to.

# Return a dict of template variables for the renderer.
return {"project": "Akhet Demo"}



The demo pushes a flash message by calling ``self.request.session.flash()``
with the message text. By default this puts the message on the "info" queue,
and it's displayed using an "info" CSS class. You can push the message onto a
different queue by specifying the queue name as a second argument. But that's
only useful if the template pops the messages from the other queue by name,
otherwise they'll never be displayed. It's customary to name the queues
according to the Python logging hierarchy: debug, info (notice), warn(ing),
error, critical. The default stylesheet defines CSS classes with distinct
styling for several of these levels.


.. include:: ../links.rst
4 changes: 2 additions & 2 deletions docs/demo/index.rst
@@ -1,9 +1,9 @@
Demo Application
Demo application
%%%%%%%%%%%%%%%%

.. toctree::
:maxdepth: 1

features
usage
content
details

0 comments on commit 1856b11

Please sign in to comment.