[Subversion] / WebHaiku / README.txt  

View of /WebHaiku/README.txt

Parent Directory | Revision Log
Revision: 2277 - (download)
Wed Jan 24 01:45:35 2007 UTC (17 years, 1 month ago) by pje
File size: 8857 byte(s)
More new features

* Redirector objects redirect to a URL with embedded vars or expressions

* @expose makes normal functions into subpages, ala Template.page()

* More tests, docs
================================================
Creating quick-and-dirty WSGI Apps with WebHaiku
================================================

WebHaiku is a very small framework for creating quick-and-dirty WSGI apps.  It
is designed for a bare minimum of startup cost and import footprint, so that it
can be used for CGI as well as long-running processes.  It requires either
Python 2.5, or Python 2.4 and the "wsgiref" package from the CheeseShop.  Apart
from ``wsgiref``, it is entirely self-contained, but it can also be used with
TurboGears/Buffet template engine plugins.  (In which case, setuptools and the
appropriate plugin(s) are also required.)


Pages and HTTP Methods
======================

WebHaiku uses classes to represent web-accessible objects::

    >>> from web_haiku import Page, test, HTML

    >>> class HelloPage(Page):
    ...     body = HTML("<h1>Hello, world!</h1>")

The above is a class representing a page whose ``GET`` response will be the
given "Hello world" message.  We can emulate browsing to this page using the
``test()`` function (which incidentally works on any WSGI application, not just
WebHaiku pages!)::

    >>> test(HelloPage)
    HTTP/1.0 200 OK
    Date: ...
    Content-Type: text/html
    ...
    <h1>Hello, world!</h1>

As you can see, we get back the expected content.  ``HEAD`` requests are also
automatically supported::

    >>> test(HelloPage, REQUEST_METHOD="HEAD")
    HTTP/1.0 200 OK
    Date: ...
    Content-Type: text/html
    Content-Length: 22
    <BLANKLINE>
    <BLANKLINE>

But ``POST`` requests are not (because we didn't define any form handlers; see
the `Form Handling`_ section, below)::

    >>> test(HelloPage, form={"a":"b"})
    HTTP/1.0 405 Method not allowed
    Date: ...
    Content-Type: text/plain
    Allow: GET, HEAD
    ...
    Excellent method!
    Alas, my response must be:
    "I cannot comply."

Notice that the response contains an automatically-generated ``Allow:`` header,
listing the HTTP methods implemented.  You can add to these allowed methods by
defining ``HTTP`` methods::

    >>> from web_haiku import HTTP
    >>> class PostHello(HelloPage):
    ...     @HTTP
    ...     def POST(self):
    ...         return self.body()  # behave the same as GET

Now ``POST`` works the same as ``GET`` for this page::

    >>> test(PostHello, form={"a":"b"})
    HTTP/1.0 200 OK
    Date: ...
    Content-Type: text/html
    ...
    <h1>Hello, world!</h1>

And the ``Allow`` for unrecognized methods will now include ``POST``::

    >>> test(PostHello, REQUEST_METHOD='PUT')
    HTTP/1.0 405 Method not allowed
    Date: ...
    Content-Type: text/plain
    Allow: GET, HEAD, POST
    ...
    Excellent method!
    Alas, my response must be:
    "I cannot comply."


Child URLs
==========

By default, pages have no child URLs, and any attempt to access such URLs
results in a 404 response::

    >>> test(HelloPage, PATH_INFO="/x")
    HTTP/1.0 404 Not Found
    Date: ...
    Content-Type: text/plain
    ...
    404 not found
    You deserve a kinder note
    Than this web haiku!
    ...

But you can create subpages using template objects, embedded ``Page`` classes,
or ``@expose`` methods (or `Dynamic Children`_ as described later below)::

    >>> from web_haiku import expose

    >>> class Container(Page):
    ...     x = HTML.page("This is page X")
    ... 
    ...     class y(Page):
    ...         body = HTML("This is page Y")
    ... 
    ...     @expose
    ...     def z(self):
    ...         self.start_response(
    ...             "200 Yeah!", [('Content-type','text/plain')]
    ...         )
    ...         return ['I be da Z!']

    >>> test(Container, PATH_INFO="/x")
    HTTP/1.0 200 OK
    ...
    This is page X

    >>> test(Container, PATH_INFO="/y")
    HTTP/1.0 200 OK
    ...
    This is page Y

    >>> test(Container, PATH_INFO="/z")
    HTTP/1.0 200 Yeah!
    ...
    I be da Z!



Dynamic Children
----------------

You can implement dynamic child URLs by overriding the ``handle_child()``
method of your pages...  XXX




Special URL Handling for Container Pages and Leaf Pages
-------------------------------------------------------

Note that even if a page has children, it must still have a ``body`` of
its own to support ``GET`` operations::

    >>> test(Container)
    HTTP/1.0 405 Method not allowed
    Date: ...
    Content-Type: text/plain
    Allow:...
    ...
    Excellent method!
    Alas, my response must be:
    "I cannot comply."

    >>> Container.body = HTML("This is the container")
    >>> test(Container)
    HTTP/1.0 200 OK
    ...
    This is the container

The URL used to access a page must end with a trailing ``/`` if it is a
container, and it must NOT end with a ``/`` if the page is NOT a container.  If
the URL is incorrect, a redirect is automatically issued::

    >>> test(Container, PATH_INFO="/x/")
    HTTP/1.0 302 Found
    Date: ...
    Content-Type: text/html
    Location: http://127.0.0.1/x
    ...
    ...<meta http-equiv="refresh" content="0;url=http://127.0.0.1/x" />...

Note that this redirection will *NOT* include any form data, so ``PUT`` or
``POST`` bodies are lost!  This redirection is intended to handle user-entered
URLs only.  Query strings, however, are included in the redirection::

    >>> test(Container, PATH_INFO="/x/", QUERY_STRING="y")
    HTTP/1.0 302 Found
    Date: ...
    Content-Type: text/html
    Location: http://127.0.0.1/x?y
    ...
    ...<meta http-equiv="refresh" content="0;url=http://127.0.0.1/x?y" />...


Redirection
===========

    >>> class InlineRedirect(Page):
    ...     def body(self):
    ...         return self.redirect('http://example.com/123')

    >>> test(InlineRedirect)
    HTTP/1.0 302 Found
    Date: ...
    Content-Type: text/html
    Location: http://example.com/123
    ...
    ...<meta http-equiv="refresh" content="0;url=http://example.com/123" />...

    >>> from web_haiku import Redirector
    >>> class TemplateRedirect(Page):
    ...     body = Redirector('http://example.com/$(?environ["REQUEST_METHOD"]?)')

    >>> test(TemplateRedirect)
    HTTP/1.0 302 Found
    Date: ...
    Content-Type: text/html
    Location: http://example.com/GET
    ...
    ...<meta http-equiv="refresh" content="0;url=http://example.com/GET" />...
    

XXX REDIRECT_TO


Form Handling
=============

    >>> from web_haiku import Text, form_handler
    >>> class TestForm(Page):
    ...     """A stupid example to test the framework"""
    ... 
    ...     form_defaults = dict(name='Joey', animal='Dog', email='joe@dog.com')
    ... 
    ...     body = form_failure = HTML("""<?xml version="1.0" encoding="iso-8859-1"?>
    ... <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    ...     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    ... <html xmlns="http://www.w3.org/1999/xhtml">
    ... <head><title>What is your favorite animal ?</title></head>
    ... <body>
    ...  $show_errors
    ...  <form method="post">
    ...   <table>
    ...    <tr><td>What is your name ?</td>
    ...        <td><input type="text" name="name" value="$(?escape(name)?)"/></td></tr>
    ...    <tr><td>What is your favorite animal ?</td>
    ...        <td><input type="text" name="animal" value="$(?escape(animal)?)"/></td></tr>
    ...    <tr><td>What is your email address ?</td>
    ...        <td><input type="text" name="email" value="$(?escape(email)?)"/></td></tr>
    ...    <tr><td colspan="2"><input type="submit" /></td></tr>
    ...   </table>
    ...  </form>
    ... </body>
    ... </html>""")
    ...     form_success = Text("Hey Joe!")
    ... 
    ...     @form_handler
    ...     def check_joe(self):
    ...         if self.name!='Joe': self.errors.append("Hey, you're not Joe!")
    ... 
    ...     errors_found = HTML.fragment(
    ...         "<p>Please correct the following problems:</p>\n"
    ...         '<ul class="form_errors"><li class="form_error">'
    ...         r'$(? "</li>\n<li class=\"form_error\">".join(errors) ?)'
    ...         '</li></ul>'
    ...     )

    >>> test(TestForm)
    HTTP/1.0 200 OK
    ...
    Content-Type: text/html
    ...
    ...What is your favorite animal ?...
    ...<input type="text" name="name" value="Joey"/>...
    ...

    >>> test(TestForm, form=dict(name="Me", email="blah"))
    HTTP/1.0 200 OK
    ...
     <p>Please correct the following problems:</p>
    <ul class="form_errors"><li class="form_error">Hey, you're not Joe!</li></ul>
    ...<input type="text" name="name" value="Me"/>...


@form_handler, .errors, .form_success, .form_failure, .parse_form(),
.form_data, .form_defaults, 


Database Access
===============

.db, .db_connect(), .cursor(), .query(), Row


Templates
=========

Text, HTML, Template, .fragment(), .page(), .method(), .http_method(),
escape(), .errors_found, .show_errors

EvalMap, EvalTemplate




cvs-admin@eby-sarna.com

Powered by ViewCVS 1.0-dev

ViewCVS and CVS Help