summaryrefslogtreecommitdiff
path: root/docs/tutorials.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/tutorials.rst')
-rw-r--r--docs/tutorials.rst933
1 files changed, 933 insertions, 0 deletions
diff --git a/docs/tutorials.rst b/docs/tutorials.rst
new file mode 100644
index 00000000..f919b318
--- /dev/null
+++ b/docs/tutorials.rst
@@ -0,0 +1,933 @@
+.. _tutorials:
+
+Tutorials
+---------
+
+
+This tutorial will walk you through basic but complete CherryPy applications
+that will show you common concepts as well as slightly more adavanced ones.
+
+.. contents::
+ :depth: 4
+
+Tutorial 1: A basic web application
+###################################
+
+The following example demonstrates the most basic application
+you could write with CherryPy. It starts a server and hosts
+an application that will be served at request reaching
+http://127.0.0.1:8080/
+
+.. code-block:: python
+ :linenos:
+
+ import cherrypy
+
+ class HelloWorld(object):
+ @cherrypy.expose
+ def index(self):
+ return "Hello world!"
+
+ if __name__ == '__main__':
+ cherrypy.quickstart(HelloWorld())
+
+Store this code snippet into a file named `tut01.py` and
+execute it as follows:
+
+.. code-block:: bash
+
+ $ python tut01.py
+
+This will display something along the following:
+
+.. code-block:: text
+ :linenos:
+
+ [24/Feb/2014:21:01:46] ENGINE Listening for SIGHUP.
+ [24/Feb/2014:21:01:46] ENGINE Listening for SIGTERM.
+ [24/Feb/2014:21:01:46] ENGINE Listening for SIGUSR1.
+ [24/Feb/2014:21:01:46] ENGINE Bus STARTING
+ CherryPy Checker:
+ The Application mounted at '' has an empty config.
+
+ [24/Feb/2014:21:01:46] ENGINE Started monitor thread 'Autoreloader'.
+ [24/Feb/2014:21:01:46] ENGINE Started monitor thread '_TimeoutMonitor'.
+ [24/Feb/2014:21:01:46] ENGINE Serving on http://127.0.0.1:8080
+ [24/Feb/2014:21:01:46] ENGINE Bus STARTED
+
+This tells you several things. The first three lines indicate
+the server will handle :mod:`signal` for you. The next line tells you
+the current state of the server, as that
+point it is in `STARTING` stage. Then, you are notified your
+application has no specific configuration set to it.
+Next, the server starts a couple of internal utilities that
+we will explain later. Finally, the server indicates it is now
+ready to accept incoming communications as it listens on
+the address `127.0.0.1:8080`. In other words, at that stage your
+application is ready to be used.
+
+Before moving on, let's discuss the message
+regarding the lack of configuration. By default, CherryPy has
+a feature which will review the syntax correctness of settings
+you could provide to configure the application. When none are
+provided, a warning message is thus displayed in the logs. That
+log is harmless and will not prevent CherryPy from working. You
+can refer to :ref:`the documentation above <perappconf>` to
+understand how to set the configuration.
+
+Tutorial 2: Different URLs lead to different functions
+######################################################
+
+Your applications will obviously handle more than a single URL.
+Let's imagine you have an application that generates a random
+string each time it is called:
+
+.. code-block:: python
+ :linenos:
+
+ import random
+ import string
+
+ import cherrypy
+
+ class StringGenerator(object):
+ @cherrypy.expose
+ def index(self):
+ return "Hello world!"
+
+ @cherrypy.expose
+ def generate(self):
+ return ''.join(random.sample(string.hexdigits, 8))
+
+ if __name__ == '__main__':
+ cherrypy.quickstart(StringGenerator())
+
+Save this into a file named `tut02.py` and run it as follows:
+
+.. code-block:: bash
+
+ $ python tut02.py
+
+Go now to http://localhost:8080/generate and your browser
+will display a random string.
+
+Let's take a minute to decompose what's happening here. This is the
+URL that you have typed into your browser: http://localhost:8080/generate
+
+This URL contains various parts:
+
+- `http://` which roughly indicates it's a URL using the HTTP protocol (see :rfc:`2616`).
+- `localhost:8080` is the server's address. It's made of a hostname and a port.
+- `/generate` which is the path segment of the URL. This is what CherryPy uses to
+ locate an :term:`exposed` function or method to respond.
+
+Here CherryPy uses the `index()` method to handle `/` and the
+`generate()` method to handle `/generate`
+
+.. _tut03:
+
+Tutorial 3: My URLs have parameters
+###################################
+
+In the previous tutorial, we have seen how to create an application
+that could generate a random string. Let's not assume you wish
+to indicate the length of that string dynamically.
+
+.. code-block:: python
+ :linenos:
+
+ import random
+ import string
+
+ import cherrypy
+
+ class StringGenerator(object):
+ @cherrypy.expose
+ def index(self):
+ return "Hello world!"
+
+ @cherrypy.expose
+ def generate(self, length=8):
+ return ''.join(random.sample(string.hexdigits, int(length)))
+
+ if __name__ == '__main__':
+ cherrypy.quickstart(StringGenerator())
+
+Save this into a file named `tut03.py` and run it as follows:
+
+.. code-block:: bash
+
+ $ python tut03.py
+
+Go now to http://localhost:8080/generate?length=16 and your browser
+will display a generated string of length 16. Notice how
+we benefit from Python's default arguments' values to support
+URLs such as http://localhost:8080/password still.
+
+In a URL such as this one, the section after `?` is called a
+query-string. Traditionally, the query-string is used to
+contextualize the URL by passing a set of (key, value) pairs. The
+format for those pairs is `key=value`. Each pair being
+separated by a `&` character.
+
+Notice how we have to convert the given `length` value to
+and integer. Indeed, values are sent out from the client
+to our server as strings.
+
+Much like CherryPy maps URL path segments to exposed functions,
+query-string keys are mapped to those exposed function parameters.
+
+.. _tut04:
+
+Tutorial 4: Submit this form
+############################
+
+CherryPy is a web framework upon which you build web applications.
+The most traditionnal shape taken by applications is through
+an HTML user-interface speaking to your CherryPy server.
+
+Let's see how to handle HTML forms via the following
+example.
+
+.. code-block:: python
+ :linenos:
+
+ import random
+ import string
+
+ import cherrypy
+
+ class StringGenerator(object):
+ @cherrypy.expose
+ def index(self):
+ return """<html>
+ <head></head>
+ <body>
+ <form method="get" action="generate">
+ <input type="text" value="8" name="length" />
+ <button type="submit">Give it now!</button>
+ </form>
+ </body>
+ </html>"""
+
+ @cherrypy.expose
+ def generate(self, length=8):
+ return ''.join(random.sample(string.hexdigits, int(length)))
+
+ if __name__ == '__main__':
+ cherrypy.quickstart(StringGenerator())
+
+Save this into a file named `tut04.py` and run it as follows:
+
+.. code-block:: bash
+
+ $ python tut04.py
+
+Go now to http://localhost:8080/ and your browser and this will
+display a simple input field to indicate the length of the string
+you want to generate.
+
+Notice that in this example, the form uses the `GET` method and
+when you pressed the `Give it now!` button, the form is sent using the
+same URL as in the :ref:`previous <tut03>` tutorial. HTML forms also support the
+`POST` method, in that case the query-string is not appended to the
+URL but it sent as the body of the client's request to the server.
+However, this would not change your application's exposed method because
+CherryPy handles both the same way and uses the exposed's handler
+parameters to deal with the query-string (key, value) pairs.
+
+.. _tut05:
+
+Tutorial 5: Track my end-user's activity
+########################################
+
+It's not uncommon that an application needs to follow the
+user's activity for a while. The usual mechanism is to use
+a `session identifier <http://en.wikipedia.org/wiki/Session_(computer_science)#HTTP_session_token>`_
+that is carried during the conversation between the user and
+your application.
+
+.. code-block:: python
+ :linenos:
+
+ import random
+ import string
+
+ import cherrypy
+
+ class StringGenerator(object):
+ @cherrypy.expose
+ def index(self):
+ return """<html>
+ <head></head>
+ <body>
+ <form method="get" action="generate">
+ <input type="text" value="8" name="length" />
+ <button type="submit">Give it now!</button>
+ </form>
+ </body>
+ </html>"""
+
+ @cherrypy.expose
+ def generate(self, length=8):
+ some_string = ''.join(random.sample(string.hexdigits, int(length)))
+ cherrypy.session['mystring'] = some_string
+ return some_string
+
+ @cherrypy.expose
+ def display(self):
+ return cherrypy.session['mystring']
+
+ if __name__ == '__main__':
+ conf = {
+ '/': {
+ 'tools.sessions.on': True
+ }
+ }
+ cherrypy.quickstart(StringGenerator(), '/', conf)
+
+Save this into a file named `tut05.py` and run it as follows:
+
+.. code-block:: bash
+
+ $ python tut05.py
+
+In this example, we generate the string as in the
+:ref:`previous <tut04>` tutorial but also store it in the current
+session. If you go to http://localhostt:8080/, generate a
+random string, then go to http://localhostt:8080/display, you
+will see the string you just generated.
+
+The lines 30-34 show you how to enable the session support
+in your CherryPy application. By default, CherryPy will save
+sessions in the process's memory. It supports more persistent
+:ref:`backends <basicsession>` as well.
+
+Tutorial 6: What about my javascripts, CSS and images?
+######################################################
+
+Web application are usually also made of static content such
+as javascript, CSS files or images. CherryPy provides support
+to serve static content to end-users.
+
+Let's assume, you want to associate a stylesheet with your
+application to display a blue background color (why not?).
+
+First, save the following stylesheet into a file named `style.css`
+and stored into a local directory `public/css`.
+
+.. code-block:: css
+ :linenos:
+
+ body {
+ background-color: blue;
+ }
+
+Now let's update the HTML code so that we link to the stylesheet
+using the http://localhost:8080/static/css/style.css URL.
+
+.. code-block:: python
+ :linenos:
+
+ import os, os.path
+ import random
+ import string
+
+ import cherrypy
+
+ class StringGenerator(object):
+ @cherrypy.expose
+ def index(self):
+ return """<html>
+ <head>
+ <link href="/static/css/style.css" rel="stylesheet">
+ </head>
+ <body>
+ <form method="get" action="generate">
+ <input type="text" value="8" name="length" />
+ <button type="submit">Give it now!</button>
+ </form>
+ </body>
+ </html>"""
+
+ @cherrypy.expose
+ def generate(self, length=8):
+ some_string = ''.join(random.sample(string.hexdigits, int(length)))
+ cherrypy.session['mystring'] = some_string
+ return some_string
+
+ @cherrypy.expose
+ def display(self):
+ return cherrypy.session['mystring']
+
+ if __name__ == '__main__':
+ conf = {
+ '/': {
+ 'tools.sessions.on': True,
+ 'tools.staticdir.root': os.path.abspath(os.getcwd())
+ },
+ '/static': {
+ 'tools.staticdir.on': True,
+ 'tools.staticdir.dir': './public'
+ }
+ }
+ cherrypy.quickstart(StringGenerator(), '/', conf)
+
+Save this into a file named `tut06.py` and run it as follows:
+
+.. code-block:: bash
+
+ $ python tut06.py
+
+Going to http://localhost:8080/, you should be greeted by a flashy blue color.
+
+CherryPy provides support to serve a single file or a complete
+directory structure. Most of the time, this is what you'll end
+up doing so this is what the code above demonstrates. First, we
+indicate the `root` directory of all of our static content. This
+must be an absolute path for security reason. CherryPy will
+complain if you provide only non-absolute paths when looking for a
+match to your URLs.
+
+Then we indicate that all URLs which path segment starts with `/static`
+will be served as static content. We map that URL to the `public`
+directory, a direct child of the `root` directory. The entire
+sub-tree of the `public` directory will be served as static content.
+CherryPy will map URLs to path within that directory. This is why
+`/static/css/style.css` is found in `public/css/style.css`.
+
+Tutorial 7: Give us a REST
+##########################
+
+It's not unusual nowadays that web applications expose some sort
+of datamodel or computation functions. Without going into
+its details, one strategy is to follow the `REST principles
+edicted by Roy T. Fielding
+<http://www.ibm.com/developerworks/library/ws-restful/index.html>`_.
+
+Roughly speaking, it assumes that you can identify a resource
+and that you can address that resource through that identifier.
+
+"What for?" you may ask. Well, mostly, these principles are there
+to ensure that you decouple, as best as you can, the entities
+your application expose from the way they are manipulated or
+consumed. To embrace this point of view, developers will
+usually design a web API that expose pairs of `(URL, HTTP method, data, constraints)`.
+
+.. note::
+
+ You will often hear REST and web API together. The former is
+ one strategy to provide the latter. This tutorial will not go
+ deeper in that whole web API concept as it's a much more
+ engaging subject, but you ought to read more about it online.
+
+
+Lets go through a small example of a very basic web API
+midly following REST principles.
+
+.. code-block:: python
+ :linenos:
+
+ import random
+ import string
+
+ import cherrypy
+
+ class StringGeneratorWebService(object):
+ exposed = True
+
+ @cherrypy.tools.accept(media='text/plain')
+ def GET(self):
+ return cherrypy.session['mystring']
+
+ def POST(self, length=8):
+ some_string = ''.join(random.sample(string.hexdigits, int(length)))
+ cherrypy.session['mystring'] = some_string
+ return some_string
+
+ def PUT(self, another_string):
+ cherrypy.session['mystring'] = another_string
+
+ def DELETE(self):
+ cherrypy.session.pop('mystring', None)
+
+ if __name__ == '__main__':
+ conf = {
+ '/': {
+ 'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
+ 'tools.sessions.on': True,
+ 'tools.response_headers.on': True,
+ 'tools.response_headers.headers': [('Content-Type', 'text/plain')],
+ }
+ }
+ cherrypy.quickstart(StringGeneratorWebService(), '/', conf)
+
+
+Save this into a file named `tut07.py` and run it as follows:
+
+.. code-block:: bash
+
+ $ python tut07.py
+
+Before we see it in action, let's explain a few things. Until now,
+CherryPy was creating a tree of exposed methods that were used to
+math URLs. In the case of our web API, we want to stress the role
+played by the actual requests' HTTP methods. So we created
+methods that are named after them and they are all exposed at once
+through the `exposed = True` attribute of the class itself.
+
+However, we must then switch from the default mechanism of matching
+URLs to method for one that is aware of the whole HTTP method
+shenanigan. This is what goes on line 27 where we create
+a :class:`~cherrypy.dispatch.MethodDispatcher` instance.
+
+Then we force the responses `content-type` to be `text/plain` and
+we finally ensure that `GET` requests will only be responded to clients
+that accept that `content-type` by having a `Accept: text/plain`
+header set in their request. However, we do this only for that
+HTTP method as it wouldn't have much meaning on the oher methods.
+
+
+For the purpose of this tutorial, we will be using a Python client
+rather than your browser as we wouldn't be able to actually try
+our web API otherwiser.
+
+Please install `requests <http://www.python-requests.org/en/latest/>`_
+through the following command:
+
+.. code-block:: bash
+
+ $ pip install requests
+
+Then fire up a Python terminal and try the following commands:
+
+.. code-block:: pycon
+ :linenos:
+
+ >>> import requests
+ >>> s = requests.Session()
+ >>> r = s.get('http://127.0.0.1:8080/')
+ >>> r.status_code
+ 500
+ >>> r = s.post('http://127.0.0.1:8080/')
+ >>> r.status_code, r.text
+ (200, u'04A92138')
+ >>> r = s.get('http://127.0.0.1:8080/')
+ >>> r.status_code, r.text
+ (200, u'04A92138')
+ >>> r = s.get('http://127.0.0.1:8080/', headers={'Accept': 'application/json'})
+ >>> r.status_code
+ 406
+ >>> r = s.put('http://127.0.0.1:8080/', params={'another_string': 'hello'})
+ >>> r = s.get('http://127.0.0.1:8080/')
+ >>> r.status_code, r.text
+ (200, u'hello')
+ >>> r = s.delete('http://127.0.0.1:8080/')
+ >>> r = s.get('http://127.0.0.1:8080/')
+ >>> r.status_code
+ 500
+
+The first and last `500` responses steam from the fact that, in
+the first case, we haven't yet generated a string through `POST` and,
+on the latter case, that it doesn't exist after we've deleted it.
+
+Lines 12-14 show you how the application reacted when our client requested
+the generated string as a JSON format. Since we configured the
+web API to only support plain text, it returns the appropriate
+`HTTP error code http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7`
+
+
+.. note::
+
+ We use the `Session <http://www.python-requests.org/en/latest/user/advanced/#session-objects>`_
+ interface of `requests` so that it takes care of carrying the
+ session id stored in the request cookie in each subsequent
+ request. That is handy.
+
+.. _tut08:
+
+
+Tutorial 8: Make it smoother with Ajax
+######################################
+
+In the recent years, web applications have moved away from the
+simple pattern of "HTML forms + refresh the whole page". This
+traditional scheme still works very well but users have become used
+to web applications that don't refresh the entire page.
+Broadly speaking, web applications carry code performed
+client-side that can speak with the backend without having to
+refresh the whole page.
+
+This tutorial will involve a little more code this time around. First,
+let's see our CSS stylesheet located in `public/css/style.css`.
+
+.. code-block:: css
+ :linenos:
+
+ body {
+ background-color: blue;
+ }
+
+ #the-string {
+ display: none;
+ }
+
+We're adding a simple rule about the element that will display
+the generated string. By default, let's not show it up.
+Save the following HTML code into a file named `index.html`.
+
+.. code-block:: html
+ :linenos:
+
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <link href="/static/css/style.css" rel="stylesheet">
+ <script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
+ <script type="text/javascript">
+ $(document).ready(function() {
+
+ $("#generate-string").click(function(e) {
+ $.post("/generator", {"length": $("input[name='length']").val()})
+ .done(function(string) {
+ $("#the-string").show();
+ $("#the-string input").val(string);
+ });
+ e.preventDefault();
+ });
+
+ $("#replace-string").click(function(e) {
+ $.ajax({
+ type: "PUT",
+ url: "/generator",
+ data: {"another_string": $("#the-string").val()}
+ })
+ .done(function() {
+ alert("Replaced!");
+ });
+ e.preventDefault();
+ });
+
+ $("#delete-string").click(function(e) {
+ $.ajax({
+ type: "DELETE",
+ url: "/generator"
+ })
+ .done(function() {
+ $("#the-string").hide();
+ });
+ e.preventDefault();
+ });
+
+ });
+ </script>
+ </head>
+ <body>
+ <input type="text" value="8" name="length" />
+ <button id="generate-string">Give it now!</button>
+ <div id="the-string">
+ <input type="text" />
+ <button id="replace-string">Replace</button>
+ <button id="delete-string">Delete it</button>
+ </div>
+ </body>
+ </html>
+
+We'll be using the `jQuery framework <http://jquery.com/>`_
+out of simplicity but feel free to replace it with your
+favourite tool. The page is composed of simple HTML elements
+to get user input and display the generated string. It also
+contains client-side code to talk to the backend API that
+actually performs the hard work.
+
+Finally, here's the application's code that serves the
+HTML page above and responds to requests to generate strings.
+Both are hosted by the same application server.
+
+.. code-block:: python
+ :linenos:
+
+ import os, os.path
+ import random
+ import string
+
+ import cherrypy
+
+ class StringGenerator(object):
+ @cherrypy.expose
+ def index(self):
+ return file('index.html')
+
+ class StringGeneratorWebService(object):
+ exposed = True
+
+ @cherrypy.tools.accept(media='text/plain')
+ def GET(self):
+ return cherrypy.session['mystring']
+
+ def POST(self, length=8):
+ some_string = ''.join(random.sample(string.hexdigits, int(length)))
+ cherrypy.session['mystring'] = some_string
+ return some_string
+
+ def PUT(self, another_string):
+ cherrypy.session['mystring'] = another_string
+
+ def DELETE(self):
+ cherrypy.session.pop('mystring', None)
+
+ if __name__ == '__main__':
+ conf = {
+ '/': {
+ 'tools.sessions.on': True,
+ 'tools.staticdir.root': os.path.abspath(os.getcwd())
+ },
+ '/generator': {
+ 'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
+ 'tools.response_headers.on': True,
+ 'tools.response_headers.headers': [('Content-Type', 'text/plain')],
+ },
+ '/static': {
+ 'tools.staticdir.on': True,
+ 'tools.staticdir.dir': './public'
+ }
+ }
+ webapp = StringGenerator()
+ webapp.generator = StringGeneratorWebService()
+ cherrypy.quickstart(webapp, '/', conf)
+
+
+Save this into a file named `tut08.py` and run it as follows:
+
+.. code-block:: bash
+
+ $ python tut08.py
+
+Go to http://127.0.0.1:8080/ and play with the input and buttons
+to generate, replace or delete the strings. Notice how the page
+isn't refreshed, simply part of its content.
+
+Notice as well how your frontend converses with the backend using
+a straightfoward, yet clean, web service API. That same API
+could easily be used by non-HTML clients.
+
+
+Tutorial 9: Data is all my life
+###############################
+
+Until now, all the generated strings were saved in the
+session, which by default is stored in the process memory. Though,
+you can persist sessions on disk or in a distributed memory store,
+this is not the right way of keeping your data on the long run.
+Sessions are there to identify your user and carry as little
+amount of data as necessary for the operation carried by the user.
+
+To store, persist and query data your need a proper database server.
+There exist many to choose from with various paradigm support:
+
+- relational: PostgreSQL, SQLite, MariaDB, Firebird
+- column-oriented: HBase, Cassandra
+- key-store: redis, memcached
+- document oriented: Couchdb, MongoDB
+- graph-oriented: neo4j
+
+Let's focus on the relational ones since they are the most common
+and probably what you will want to learn first.
+
+For the sake of reducing the number of dependencies for these
+tutorials, we will go for the :mod:`sqlite` database which
+is directly supported by Python.
+
+Our application will replace the storage of the generated
+string from the session to a SQLite database. The application
+will have the same HTML code as :ref:`tutorial 08 <tut08>`.
+So let's simply focus on the application code itself:
+
+.. code-block:: python
+ :linenos:
+
+ import os, os.path
+ import random
+ import sqlite3
+ import string
+
+ import cherrypy
+
+ DB_STRING = "my.db"
+
+ class StringGenerator(object):
+ @cherrypy.expose
+ def index(self):
+ return file('index.html')
+
+ class StringGeneratorWebService(object):
+ exposed = True
+
+ @cherrypy.tools.accept(media='text/plain')
+ def GET(self):
+ with sqlite3.connect(DB_STRING) as c:
+ c.execute("SELECT value FROM user_string WHERE session_id=?",
+ [cherrypy.session.id])
+ return c.fetchone()
+
+ def POST(self, length=8):
+ some_string = ''.join(random.sample(string.hexdigits, int(length)))
+ with sqlite3.connect(DB_STRING) as c:
+ c.execute("INSERT INTO user_string VALUES (?, ?)",
+ [cherrypy.session.id, some_string])
+ return some_string
+
+ def PUT(self, another_string):
+ with sqlite3.connect(DB_STRING) as c:
+ c.execute("UPDATE user_string SET value=? WHERE session_id=?",
+ [another_string, cherrypy.session.id])
+
+ def DELETE(self):
+ with sqlite3.connect(DB_STRING) as c:
+ c.execute("DELETE FROM user_string WHERE session_id=?",
+ [cherrypy.session.id])
+
+ def setup_database():
+ """
+ Create the `user_string` table in the database
+ on server startup
+ """
+ with sqlite3.connect(DB_STRING) as con:
+ con.execute("CREATE TABLE user_string (session_id, value)")
+
+ def cleanup_database():
+ """
+ Destroy the `user_string` table from the database
+ on server shutdown.
+ """
+ with sqlite3.connect(DB_STRING) as con:
+ con.execute("DROP TABLE user_string")
+
+ if __name__ == '__main__':
+ conf = {
+ '/': {
+ 'tools.sessions.on': True,
+ 'tools.staticdir.root': os.path.abspath(os.getcwd())
+ },
+ '/generator': {
+ 'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
+ 'tools.response_headers.on': True,
+ 'tools.response_headers.headers': [('Content-Type', 'text/plain')],
+ },
+ '/static': {
+ 'tools.staticdir.on': True,
+ 'tools.staticdir.dir': './public'
+ }
+ }
+
+ cherrypy.engine.subscribe('start', setup_database)
+ cherrypy.engine.subscribe('stop', cleanup_database)
+
+ webapp = StringGenerator()
+ webapp.generator = StringGeneratorWebService()
+ cherrypy.quickstart(webapp, '/', conf)
+
+
+Save this into a file named `tut09.py` and run it as follows:
+
+.. code-block:: bash
+
+ $ python tut09.py
+
+Let's first see how we create two functions that create
+and destroy the table within our database. These functions
+are registered to the CherryPy's server on lines 76-77,
+so that they are called when the server starts and stops.
+
+Next, notice how we replaced all the session code with calls
+to the database. We use the session id to identify the
+user's string within our database. Since the session will go
+away after a while, it's probably not the right approach.
+A better idea would be to associate the user's login or
+more resilient unique identifier. For the sake of our
+demo, this should do.
+
+.. note::
+
+ Unfortunately, sqlite in Python forbids us
+ to share a connection between threads. Since CherryPy is a
+ multi-threaded server, this would be an issue. This is the
+ reason why we open and close a connection to the database
+ on each call. This is clearly not really production friendly,
+ and it is probably advisable to either use a more capable
+ database engine or a higher level library, such as
+ `SQLAlchemy <http://sqlalchemy.readthedocs.org>`_, to better
+ support your application's needs.
+
+
+Tutorial 10: Organize my code
+#############################
+
+CherryPy comes with a powerful architecture
+that helps you organizing your code in a way that should make
+it easier to maintain and more flexible.
+
+Several mechanisms are at your disposal, this tutorial will focus
+on the three main ones:
+
+- :ref:`dispatchers <dispatchers>`
+- :ref:`tools <tools>`
+- :ref:`plugins <busplugins>`
+
+In order to understand them, let's imagine you are at a superstore:
+
+- You have several tills and people queuing for each of them (those are your requests)
+- You have various sections with food and other stuff (these are your data)
+- Finally you have the superstore people and their daily tasks
+ to make sure sections are always in order (this is your backend)
+
+In spite of being really simplistic, this is not far from how your
+application behaves. CherryPy helps your structure your application
+in a way that mirrors these high-level ideas.
+
+Dispatchers
+^^^^^^^^^^^
+
+Coming back to the superstore example, it is likely that you will
+want to perform operations based on the till:
+
+- Have a till for baskets with less than ten items
+- Have a till for disabled people
+- Have a till for pregnant women
+- Have a till where you can only using the store card
+
+To support these use-cases, CherryPy provides a mechanism called
+a :ref:`dispatcher <dispatchers>`. A dispatcher is executed early
+during the request processing in order to determine which piece of
+code of your application will handle the incoming request. Or, to
+continue on the store analogy, a dispatcher will decide which
+till to lead a customer to.
+
+Tools
+^^^^^
+
+Let's assume your store has decided to operate a discount spree but,
+only for a specific category of customers. CherryPy will deal
+with such use case via a mechanism called a :ref:`tool <tools>`.
+
+A tool is a piece of code that runs on a per-request
+basis in order to perform additional work. Usually a tool is a
+simple Python function that is executed at a given point during
+the process of the request by CherryPy.
+
+Plugins
+^^^^^^^
+
+As we have seen, the store has a crew of people dedicated to manage
+the stock and deal with any customers' expectation.
+
+In the CherryPy world, this translates into having functions
+that run outside of any request life-cycle. These functions should
+take care of background tasks, long lived connections (such as
+those to a database for instance), etc.
+
+:ref:`Plugins <busplugins>` are called that way because
+they work along with the CherryPy :ref:`engine <cpengine>`
+and extend it with your operations.
+
+