summaryrefslogtreecommitdiff
path: root/docs/blog-tutorial.txt
diff options
context:
space:
mode:
authorianb <devnull@localhost>2005-09-27 04:58:55 +0000
committerianb <devnull@localhost>2005-09-27 04:58:55 +0000
commite937a4b3769d7a879926f88bd4d29f4201afec4e (patch)
tree7fc05cccea06f7258fd74bac49b8ea47b3f78789 /docs/blog-tutorial.txt
parent1ca42490423a1ab8f431732716eb038786e8a78b (diff)
downloadpaste-e937a4b3769d7a879926f88bd4d29f4201afec4e.tar.gz
Refactored and culled documentation
Diffstat (limited to 'docs/blog-tutorial.txt')
-rw-r--r--docs/blog-tutorial.txt374
1 files changed, 0 insertions, 374 deletions
diff --git a/docs/blog-tutorial.txt b/docs/blog-tutorial.txt
deleted file mode 100644
index d0931e9..0000000
--- a/docs/blog-tutorial.txt
+++ /dev/null
@@ -1,374 +0,0 @@
-+++++++++++++
-Blog Tutorial
-+++++++++++++
-
-:author: Ian Bicking <ianb@colorstudy.com>
-:revision: $Rev$
-:date: $LastChangedDate$
-
-.. contents::
-
-.. note::
-
- This tutorial is not yet finished. What you see is what you get,
- and yeah that's not a whole lot.
-
-Introduction
-============
-
-This tutorial will go through the process of creating a blog using
-`Python Paste <http://pythonpaste.org>`_, SQLObject_, and `Zope Page
-Templates`_. This blog will rely heavily on static publishing -- that
-is, when at all possible flat HTML pages will be written to disk. For
-some parts (e.g., posting a new item) this will of course be
-infeasible, but for most of the site this should work fine.
-
-.. _SQLObject: http://sqlobject.org
-.. _Zope Page Templates: http://www.zope.org/DevHome/Wikis/DevSite/Projects/ZPT/FrontPage
-
-As much as possible, this code will be accompanied by unit tests, and
-test-driven methodologies. Doing test-driven documenting of the
-incremental process of creating test-driven software may get a little
-hairy, but wish me luck!
-
-This tutorial presupposes you are somewhat comfortable with the basic
-stack -- the `To-Do Tutorial`_ is a better place to start for a
-beginner.
-
-.. _To-Do Tutorial: TodoTutorial.html
-
-Setting Up The App
-==================
-
-.. note::
-
- We're doing all this in the Python interpreter, even though you'd normally do
- some of this in the shell. This way the authors of this tutorial
- can use something called doctest_, which allows this tutorial to
- be tested Python in an automated way.
-
- .. _doctest: http://python.org/doc/current/lib/module-doctest.html
-
-.. comment:
-
- >>> from paste.tests.doctest_webapp import *
- >>> BASE = '/var/www/example-builds/wwblog'
- >>> import sys
- >>> clear_dir(BASE)
- >>> run("paster create --template=webkit_zpt %s" % BASE)
- >>> os.chdir(BASE)
-
-::
-
- $ export PYTHONPATH=/path/to/Paste:$PYTHONPATH
- $ BASE=/var/www/example-builds/wwblog
- $ paster create --template=webkit_zpt $BASE
- $ cd $BASE
-
-The Model
-=========
-
-Since we're using SQLObject, we'll be doing the complete model in
-that. The predecessor of this blog used flat files, custom-written
-indexes, and simple rfc822_ based files for structure. It did not
-scale well at all.
-
-.. _rfc822: http://python.org/doc/current/lib/module-rfc822.html
-
-Here's the model:
-
-.. run:
-
- create_file('db.py', 'v1', r"""
- from sqlobject import *
-
- class Article(SQLObject):
- url = StringCol(notNull=True)
- title = StringCol()
- content = StringCol(notNull=True)
- content_mime_type = StringCol(notNull=True)
- author = ForeignKey('User')
- parent = ForeignKey('Article', default=None)
- created = DateTimeCol(notNull=True, default=DateTimeCol.now)
- last_updated = DateTimeCol(default=None)
- atom_id = StringCol()
- hidden = BoolCol(notNull=True, default=False)
- article_type = StringCol(notNull=True, default='article')
- categories = RelatedJoin('Category')
-
- class Category(SQLObject):
- name = StringCol(alternateID=True)
- articles = RelatedJoin('Article')
-
- class User(SQLObject):
- class sqlmeta:
- table = 'user_info'
- username = StringCol(alternateID=True)
- email = StringCol()
- name = StringCol()
- homepage = StringCol()
- password_encoded = StringCol()
- role = StringCol(notNull=True, default='user')
- """)
-
-.. raw:: html
- :file: resources/blog-tutorial/db.py.v1.gen.html
-
-A few things to note:
-
-* All the columns allow ``NULL`` by default, unless we say
- ``notNull=True``.
-
-* ``ForeignKey('User')`` is a join to another table (the ``User``
- table, of course). We have to use strings to refer to other class,
- because in this case the ``User`` class hasn't even been created.
- Generally all references between classes are by name.
-
-* ``created`` has a default. You can give a fixed default (like
- ``True`` or ``3``), or you can pass in a function that is called.
- In this case, if you don't indicate ``Article(...,
- created=something)`` then ``created`` will be the current date and
- time. Unless a default is explicitly given, it is an error to leave
- a column out of the constructor. ``NULL`` (which is ``None`` in
- Python) is *not* considered a default.
-
-* Some column types don't relate directly to database types. For
- instance, though PostgreSQL has a ``BOOLEAN`` type, most databases
- don't, so ``BoolCol`` translates to some kind of ``INT`` column on
- those database.
-
-* ``RelatedJoin('Category')`` creates a mapping table
- (``article_category``) and is a many-to-many join between articles
- and categories.
-
-* ``user`` isn't a valid table name in many databases, so while the
- class is named ``User``, the table actually is ``user_info``. This
- kind of extra information about a class is typically passed in
- through the ``sqlmeta`` inner class.
-
-These classes have lots of other *behavior*, but this should be a good
-list of actual information. We'll add more behavior later.
-
-Now we'll create the database. First we configure it, adding these
-lines to ``server.conf``::
-
- import os
- database = 'sqlite:%s/data.db' % os.path.dirname(__file__)
-
-You could also use::
-
- database = 'mysql://user:passwd@localhost/dbname'
- database = 'postgresql://user:password@localhost/dbname'
-
-.. comment (change server.conf)
-
- >>> change_file('server.conf', [('insert', 2, r"""import os
- ... database = 'sqlite:%s/data.db' % os.path.dirname(__file__)
- ...
- ... """)])
-
-Now we'll use ``sqlobject-admin`` to set up the tables:
-
-.. comment (do it)
-
- >>> run_command('sqlobject-admin create -f server.conf '
- ... '-m wwblog.db', 'create', and_print=True)
-
-
-.. raw:: html
- :file: resources/blog-tutorial/shell-command.create.gen.html
-
-Fixture Data
-------------
-
-To test things later we'll need a bit of data to make the tests
-interesting. It's best if we write code to clear any data and put
-known data in -- that way we can restore the database at any time to a
-known state, and can write our tests against that data.
-
-We'll add some code to the end of ``db.py``:
-
-.. comment (change)
-
- >>> append_to_file('db.py', 'append-fixture', r"""
- ...
- ... from paste import CONFIG
- ... def reset_data():
- ... sqlhub.processConnection = connectionForURI(CONFIG['database'])
- ... for soClass in (User, Category, Article):
- ... soClass.clearTable()
- ... auth = User(username='author', email='author@example.com',
- ... name='Author Person', password_encoded=None,
- ... role='author', homepage=None)
- ... user = User(username='commentor', email='comment@example.com',
- ... name='Comment Person', password_encoded=None,
- ... role='user', homepage='http://yahoo.com')
- ... programming = Category(name='Programming')
- ... family = Category(name='family')
- ... a1 = Article(url='/2004/05/01/article1.html',
- ... title='First article',
- ... content='This is an article',
- ... content_mime_type='text/html',
- ... author=auth, parent=None,
- ... last_updated=None, atom_id=None)
- ... a2 = Article(url='/2004/05/10/article2.html',
- ... title='Second article',
- ... content='Another\narticle',
- ... content_mime_type='text/plain',
- ... author=auth, parent=None,
- ... last_updated=None, atom_id=None)
- ... c1 = Article(url='/2004/05/01/article1-comment1.html',
- ... title=None, content='Nice article!',
- ... content_mime_type='text/x-untrusted-html',
- ... author=user, parent=a1, last_updated=None,
- ... atom_id=None, article_type='comment')
- ... a1.addCategory(programming)
- ... a1.addCategory(family)
- ... """)
-
-.. raw:: html
- :file: resources/blog-tutorial/db.py.append-fixture.gen.html
-
-Test Fixture
-------------
-
-See `Testing Applications With Paste <testing-applications.html>`_ for
-more on the details of how we set up testing. We'll be using `py.test
-<http://codespeak.net/py/current/doc/test.html>`_ for the testing
-framework.
-
-First, lets create our own test fixture. We'll create a directory
-``tests/`` and add a file ``fixture.py``.
-
-.. comment (create files):
-
- >>> create_file('tests/__init__.py', 'v1', '#\n')
- >>> create_file('tests/fixture.py', 'v1', r"""
- ... from paste.tests.fixture import setup_module as paste_setup
- ... from wwblog import db
- ...
- ... def setup_module(module):
- ... paste_setup(module)
- ... db.reset_data()
- ... """)
-
-.. raw:: html
- :file: resources/blog-tutorial/tests/fixture.py.v1.gen.html
-
-Now in each test we'll do::
-
- from wwblog.tests.fixture import setup_module
-
-And that will give us a consistent state for the module (note that
-data isn't reset between each test in the module, just once for the
-module, so we'll have to be aware of that).
-
-Let's write a first test:
-
-.. comment (create test):
-
- >>> create_file('tests/test_db.py', 'v1', r"""
- ... from fixture import setup_module
- ... from wwblog.db import *
- ...
- ... def test_data():
- ... # make sure we have the two users we set up
- ... assert len(list(User.select())) == 2
- ... # and get the first article for testing
- ... a1 = list(Article.selectBy(title='First article'))[0]
- ... # make sure it has categories, then make sure the
- ... # categories contain this article as well
- ... assert len(list(a1.categories)) == 2
- ... for cat in a1.categories:
- ... assert a1 in cat.articles
- ... """)
-
-.. raw:: html
- :file: resources/blog-tutorial/tests/test_db.py.v1.gen.html
-
-For the most part, this stuff is already tested by SQLObject, but this
-is a basic sanity check, and a test that we have set up the classes
-properly. One problem, though, is that we have to make sure that
-``sys.path`` is set up properly. We could set ``$PYTHONPATH``, but
-that can be a bit annoying; we'll put it in a special file
-``conftest.py`` that py.test loads up:
-
-.. comment (create):
-
- >>> create_file('conftest.py', 'v1', r"""
- ... import sys, os
- ... sys.path.append('/path/to/Paste')
- ... sys.path.append(os.path.dirname(os.path.dirname(__file__)))
- ... from paste.util.thirdparty import add_package
- ... add_package('sqlobject')
- ... """)
-
-.. raw:: html
- :file: resources/blog-tutorial/conftest.py.v1.gen.html
-
-Now we should be able to run py.test:
-
-.. comment (do):
-
- >>> run_command('py.test', 't1', and_print=True)
- inserting into sys.path: ...
- =...= test process starts =...=
- testing-mode: inprocess
- executable: .../python (...)
- using py lib: .../py <rev ...>
- .../test_db.py[1] .
- =...= tests finished: 1 passed in ... seconds =...=
-
-.. raw:: html
- :file: resources/blog-tutorial/shell-command.t1.gen.html
-
-Very good! Alright then, on to making an application...
-
-Static Publishing
------------------
-
-Remember I said something about static publishing? So... what does
-that mean?
-
-Well, it means that when possible we should write files out to disk.
-These files might be in their final form, though in some environments
-it might be nice to write out files that are interpreted by `server
-side includes <http://httpd.apache.org/docs/mod/mod_include.html>`_ or
-PHP.
-
-By generating what is effectively code, the "static" files can contain
-dynamic portions, e.g., a running list of recently-updated external
-blogs. But more importantly, many changes can be made without
-generating the entire site; changes to the look of the site, of
-course, but also smaller things, like up-to-date archive links on the
-sides of pages and other little bits of code.
-
-This tutorial will work with server-side includes, because they are
-dumb enough that we won't be tempted to push too much functionality
-into them (and we might be able to extract their functionality into
-fully-formed pages); but they'll also save us a lot of work early on.
-If you haven't used server-side includes you can read the document I
-linked too, but you can also probably pick it up easily enough from
-the examples.
-
-URL Layout
-----------
-
-The blog "application" will be available through some URL (which we
-can figure out at runtime). But everything else gets written onto
-disk with some URL equivalent, so that path and URL will have to be
-configurable.
-
-The Front Page
---------------
-
-First we'll set up a simple front page. We'll write a new
-``index.py``:
-
-.. comment (do so):
-
- >>> create_file('web/index.py', 'v1', r"""
- ... """)
-
-.. raw:: html
- :file: resources/blog-tutorial/web/index.py.v1.gen.html