1.. _tutorials:
2
3Tutorials
4---------
5
6
7This tutorial will walk you through basic but complete CherryPy applications
8that will show you common concepts as well as slightly more advanced ones.
9
10.. contents::
11   :depth:  4
12
13Tutorial 1: A basic web application
14###################################
15
16The following example demonstrates the most basic application
17you could write with CherryPy. It starts a server and hosts
18an application that will be served at request reaching
19http://127.0.0.1:8080/
20
21.. code-block:: python
22   :linenos:
23
24   import cherrypy
25
26
27   class HelloWorld(object):
28       @cherrypy.expose
29       def index(self):
30           return "Hello world!"
31
32
33   if __name__ == '__main__':
34       cherrypy.quickstart(HelloWorld())
35
36Store this code snippet into a file named `tut01.py` and
37execute it as follows:
38
39.. code-block:: bash
40
41   $ python tut01.py
42
43This will display something along the following:
44
45.. code-block:: text
46   :linenos:
47
48   [24/Feb/2014:21:01:46] ENGINE Listening for SIGHUP.
49   [24/Feb/2014:21:01:46] ENGINE Listening for SIGTERM.
50   [24/Feb/2014:21:01:46] ENGINE Listening for SIGUSR1.
51   [24/Feb/2014:21:01:46] ENGINE Bus STARTING
52   CherryPy Checker:
53   The Application mounted at '' has an empty config.
54
55   [24/Feb/2014:21:01:46] ENGINE Started monitor thread 'Autoreloader'.
56   [24/Feb/2014:21:01:46] ENGINE Serving on http://127.0.0.1:8080
57   [24/Feb/2014:21:01:46] ENGINE Bus STARTED
58
59This tells you several things. The first three lines indicate
60the server will handle :mod:`signal` for you. The next line tells you
61the current state of the server, as that
62point it is in `STARTING` stage. Then, you are notified your
63application has no specific configuration set to it.
64Next, the server starts a couple of internal utilities that
65we will explain later. Finally, the server indicates it is now
66ready to accept incoming communications as it listens on
67the address `127.0.0.1:8080`. In other words, at that stage your
68application is ready to be used.
69
70Before moving on, let's discuss the message
71regarding the lack of configuration. By default, CherryPy has
72a feature which will review the syntax correctness of settings
73you could provide to configure the application. When none are
74provided, a warning message is thus displayed in the logs. That
75log is harmless and will not prevent CherryPy from working. You
76can refer to :ref:`the documentation above <perappconf>` to
77understand how to set the configuration.
78
79Tutorial 2: Different URLs lead to different functions
80######################################################
81
82Your applications will obviously handle more than a single URL.
83Let's imagine you have an application that generates a random
84string each time it is called:
85
86.. code-block:: python
87   :linenos:
88
89   import random
90   import string
91
92   import cherrypy
93
94
95   class StringGenerator(object):
96       @cherrypy.expose
97       def index(self):
98           return "Hello world!"
99
100       @cherrypy.expose
101       def generate(self):
102           return ''.join(random.sample(string.hexdigits, 8))
103
104
105   if __name__ == '__main__':
106       cherrypy.quickstart(StringGenerator())
107
108Save this into a file named `tut02.py` and run it as follows:
109
110.. code-block:: bash
111
112   $ python tut02.py
113
114Go now to http://localhost:8080/generate and your browser
115will display a random string.
116
117Let's take a minute to decompose what's happening here. This is the
118URL that you have typed into your browser: http://localhost:8080/generate
119
120This URL contains various parts:
121
122- `http://` which roughly indicates it's a URL using the HTTP protocol (see :rfc:`2616`).
123- `localhost:8080` is the server's address. It's made of a hostname and a port.
124- `/generate` which is the path segment of the URL. This is what CherryPy uses to
125  locate an :term:`exposed` function or method to respond.
126
127Here CherryPy uses the `index()` method to handle `/` and the
128`generate()` method to handle `/generate`
129
130.. _tut03:
131
132Tutorial 3: My URLs have parameters
133###################################
134
135In the previous tutorial, we have seen how to create an application
136that could generate a random string. Let's now assume you wish
137to indicate the length of that string dynamically.
138
139.. code-block:: python
140   :linenos:
141
142   import random
143   import string
144
145   import cherrypy
146
147
148   class StringGenerator(object):
149       @cherrypy.expose
150       def index(self):
151           return "Hello world!"
152
153       @cherrypy.expose
154       def generate(self, length=8):
155           return ''.join(random.sample(string.hexdigits, int(length)))
156
157
158   if __name__ == '__main__':
159       cherrypy.quickstart(StringGenerator())
160
161Save this into a file named `tut03.py` and run it as follows:
162
163.. code-block:: bash
164
165   $ python tut03.py
166
167Go now to http://localhost:8080/generate?length=16 and your browser
168will display a generated string of length 16. Notice how
169we benefit from Python's default arguments' values to support
170URLs such as http://localhost:8080/generate still.
171
172In a URL such as this one, the section after `?` is called a
173query-string. Traditionally, the query-string is used to
174contextualize the URL by passing a set of (key, value) pairs. The
175format for those pairs is `key=value`. Each pair being
176separated by a `&` character.
177
178Notice how we have to convert the given `length` value to
179an integer. Indeed, values are sent out from the client
180to our server as strings.
181
182Much like CherryPy maps URL path segments to exposed functions,
183query-string keys are mapped to those exposed function parameters.
184
185.. _tut04:
186
187Tutorial 4: Submit this form
188############################
189
190CherryPy is a web framework upon which you build web applications.
191The most traditional shape taken by applications is through
192an HTML user-interface speaking to your CherryPy server.
193
194Let's see how to handle HTML forms via the following
195example.
196
197.. code-block:: python
198   :linenos:
199
200   import random
201   import string
202
203   import cherrypy
204
205
206   class StringGenerator(object):
207       @cherrypy.expose
208       def index(self):
209           return """<html>
210             <head></head>
211             <body>
212               <form method="get" action="generate">
213                 <input type="text" value="8" name="length" />
214                 <button type="submit">Give it now!</button>
215               </form>
216             </body>
217           </html>"""
218
219       @cherrypy.expose
220       def generate(self, length=8):
221           return ''.join(random.sample(string.hexdigits, int(length)))
222
223
224   if __name__ == '__main__':
225       cherrypy.quickstart(StringGenerator())
226
227Save this into a file named `tut04.py` and run it as follows:
228
229.. code-block:: bash
230
231   $ python tut04.py
232
233Go now to http://localhost:8080/ and your browser and this will
234display a simple input field to indicate the length of the string
235you want to generate.
236
237Notice that in this example, the form uses the `GET` method and
238when you pressed the `Give it now!` button, the form is sent using the
239same URL as in the :ref:`previous <tut03>` tutorial. HTML forms also support the
240`POST` method, in that case the query-string is not appended to the
241URL but it sent as the body of the client's request to the server.
242However, this would not change your application's exposed method because
243CherryPy handles both the same way and uses the exposed's handler
244parameters to deal with the query-string (key, value) pairs.
245
246.. _tut05:
247
248Tutorial 5: Track my end-user's activity
249########################################
250
251It's not uncommon that an application needs to follow the
252user's activity for a while. The usual mechanism is to use
253a `session identifier <http://en.wikipedia.org/wiki/Session_(computer_science)#HTTP_session_token>`_
254that is carried during the conversation between the user and
255your application.
256
257.. code-block:: python
258   :linenos:
259
260   import random
261   import string
262
263   import cherrypy
264
265
266   class StringGenerator(object):
267       @cherrypy.expose
268       def index(self):
269           return """<html>
270             <head></head>
271             <body>
272               <form method="get" action="generate">
273                 <input type="text" value="8" name="length" />
274                 <button type="submit">Give it now!</button>
275               </form>
276             </body>
277           </html>"""
278
279       @cherrypy.expose
280       def generate(self, length=8):
281           some_string = ''.join(random.sample(string.hexdigits, int(length)))
282           cherrypy.session['mystring'] = some_string
283           return some_string
284
285       @cherrypy.expose
286       def display(self):
287           return cherrypy.session['mystring']
288
289
290   if __name__ == '__main__':
291       conf = {
292           '/': {
293               'tools.sessions.on': True
294           }
295       }
296       cherrypy.quickstart(StringGenerator(), '/', conf)
297
298Save this into a file named `tut05.py` and run it as follows:
299
300.. code-block:: bash
301
302   $ python tut05.py
303
304In this example, we generate the string as in the
305:ref:`previous <tut04>` tutorial but also store it in the current
306session. If you go to http://localhost:8080/, generate a
307random string, then go to http://localhost:8080/display, you
308will see the string you just generated.
309
310The lines 30-34 show you how to enable the session support
311in your CherryPy application. By default, CherryPy will save
312sessions in the process's memory. It supports more persistent
313:ref:`backends <basicsession>` as well.
314
315Tutorial 6: What about my javascripts, CSS and images?
316######################################################
317
318Web applications are usually also made of static content such
319as javascript, CSS files or images. CherryPy provides support
320to serve static content to end-users.
321
322Let's assume, you want to associate a stylesheet with your
323application to display a blue background color (why not?).
324
325First, save the following stylesheet into a file named `style.css`
326and stored into a local directory `public/css`.
327
328.. code-block:: css
329   :linenos:
330
331   body {
332     background-color: blue;
333   }
334
335Now let's update the HTML code so that we link to the stylesheet
336using the http://localhost:8080/static/css/style.css URL.
337
338.. code-block:: python
339   :linenos:
340
341   import os, os.path
342   import random
343   import string
344
345   import cherrypy
346
347
348   class StringGenerator(object):
349       @cherrypy.expose
350       def index(self):
351           return """<html>
352             <head>
353               <link href="/static/css/style.css" rel="stylesheet">
354             </head>
355             <body>
356               <form method="get" action="generate">
357                 <input type="text" value="8" name="length" />
358                 <button type="submit">Give it now!</button>
359               </form>
360             </body>
361           </html>"""
362
363       @cherrypy.expose
364       def generate(self, length=8):
365           some_string = ''.join(random.sample(string.hexdigits, int(length)))
366           cherrypy.session['mystring'] = some_string
367           return some_string
368
369       @cherrypy.expose
370       def display(self):
371           return cherrypy.session['mystring']
372
373
374   if __name__ == '__main__':
375       conf = {
376           '/': {
377               'tools.sessions.on': True,
378               'tools.staticdir.root': os.path.abspath(os.getcwd())
379           },
380           '/static': {
381               'tools.staticdir.on': True,
382               'tools.staticdir.dir': './public'
383           }
384       }
385       cherrypy.quickstart(StringGenerator(), '/', conf)
386
387
388Save this into a file named `tut06.py` and run it as follows:
389
390.. code-block:: bash
391
392   $ python tut06.py
393
394Going to http://localhost:8080/, you should be greeted by a flashy blue color.
395
396CherryPy provides support to serve a single file or a complete
397directory structure. Most of the time, this is what you'll end
398up doing so this is what the code above demonstrates. First, we
399indicate the `root` directory of all of our static content. This
400must be an absolute path for security reason. CherryPy will
401complain if you provide only relative paths when looking for a
402match to your URLs.
403
404Then we indicate that all URLs which path segment starts with `/static`
405will be served as static content. We map that URL to the `public`
406directory, a direct child of the `root` directory. The entire
407sub-tree of the `public` directory will be served as static content.
408CherryPy will map URLs to path within that directory. This is why
409`/static/css/style.css` is found in `public/css/style.css`.
410
411Tutorial 7: Give us a REST
412##########################
413
414It's not unusual nowadays that web applications expose some sort
415of datamodel or computation functions. Without going into
416its details, one strategy is to follow the `REST principles
417edicted by Roy T. Fielding
418<http://www.ibm.com/developerworks/library/ws-restful/index.html>`_.
419
420Roughly speaking, it assumes that you can identify a resource
421and that you can address that resource through that identifier.
422
423"What for?" you may ask. Well, mostly, these principles are there
424to ensure that you decouple, as best as you can, the entities
425your application expose from the way they are manipulated or
426consumed. To embrace this point of view, developers will
427usually design a web API that expose pairs of `(URL, HTTP method, data, constraints)`.
428
429.. note::
430
431   You will often hear REST and web API together. The former is
432   one strategy to provide the latter. This tutorial will not go
433   deeper in that whole web API concept as it's a much more
434   engaging subject, but you ought to read more about it online.
435
436
437Lets go through a small example of a very basic web API
438mildly following REST principles.
439
440.. code-block:: python
441   :linenos:
442
443   import random
444   import string
445
446   import cherrypy
447
448
449   @cherrypy.expose
450   class StringGeneratorWebService(object):
451
452       @cherrypy.tools.accept(media='text/plain')
453       def GET(self):
454           return cherrypy.session['mystring']
455
456       def POST(self, length=8):
457           some_string = ''.join(random.sample(string.hexdigits, int(length)))
458           cherrypy.session['mystring'] = some_string
459           return some_string
460
461       def PUT(self, another_string):
462           cherrypy.session['mystring'] = another_string
463
464       def DELETE(self):
465           cherrypy.session.pop('mystring', None)
466
467
468   if __name__ == '__main__':
469       conf = {
470           '/': {
471               'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
472               'tools.sessions.on': True,
473               'tools.response_headers.on': True,
474               'tools.response_headers.headers': [('Content-Type', 'text/plain')],
475           }
476       }
477       cherrypy.quickstart(StringGeneratorWebService(), '/', conf)
478
479
480Save this into a file named `tut07.py` and run it as follows:
481
482.. code-block:: bash
483
484   $ python tut07.py
485
486Before we see it in action, let's explain a few things. Until now,
487CherryPy was creating a tree of exposed methods that were used to
488match URLs. In the case of our web API, we want to stress the role
489played by the actual requests' HTTP methods. So we created
490methods that are named after them and they are all exposed at once
491by decorating the class itself with `cherrypy.expose`.
492
493However, we must then switch from the default mechanism of matching
494URLs to method for one that is aware of the whole HTTP method
495shenanigan. This is what goes on line 27 where we create
496a :class:`~cherrypy.dispatch.MethodDispatcher` instance.
497
498Then we force the responses `content-type` to be `text/plain` and
499we finally ensure that `GET` requests will only be responded to clients
500that accept that `content-type` by having a `Accept: text/plain`
501header set in their request. However, we do this only for that
502HTTP method as it wouldn't have much meaning on the other methods.
503
504
505For the purpose of this tutorial, we will be using a Python client
506rather than your browser as we wouldn't be able to actually try
507our web API otherwise.
508
509Please install `requests <http://www.python-requests.org/en/latest/>`_
510through the following command:
511
512.. code-block:: bash
513
514   $ pip install requests
515
516Then fire up a Python terminal and try the following commands:
517
518.. code-block:: pycon
519   :linenos:
520
521   >>> import requests
522   >>> s = requests.Session()
523   >>> r = s.get('http://127.0.0.1:8080/')
524   >>> r.status_code
525   500
526   >>> r = s.post('http://127.0.0.1:8080/')
527   >>> r.status_code, r.text
528   (200, u'04A92138')
529   >>> r = s.get('http://127.0.0.1:8080/')
530   >>> r.status_code, r.text
531   (200, u'04A92138')
532   >>> r = s.get('http://127.0.0.1:8080/', headers={'Accept': 'application/json'})
533   >>> r.status_code
534   406
535   >>> r = s.put('http://127.0.0.1:8080/', params={'another_string': 'hello'})
536   >>> r = s.get('http://127.0.0.1:8080/')
537   >>> r.status_code, r.text
538   (200, u'hello')
539   >>> r = s.delete('http://127.0.0.1:8080/')
540   >>> r = s.get('http://127.0.0.1:8080/')
541   >>> r.status_code
542   500
543
544The first and last `500` responses stem from the fact that, in
545the first case, we haven't yet generated a string through `POST` and,
546on the latter case, that it doesn't exist after we've deleted it.
547
548Lines 12-14 show you how the application reacted when our client requested
549the generated string as a JSON format. Since we configured the
550web API to only support plain text, it returns the appropriate
551`HTTP error code <http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7>`_.
552
553
554.. note::
555
556   We use the `Session <http://www.python-requests.org/en/latest/user/advanced/#session-objects>`_
557   interface of `requests` so that it takes care of carrying the
558   session id stored in the request cookie in each subsequent
559   request. That is handy.
560
561.. important::
562
563   It's all about RESTful URLs these days, isn't it?
564
565   It is likely your URL will be made of dynamic parts that you
566   will not be able to match to page handlers. For example,
567   ``/library/12/book/15`` cannot be directly handled by the
568   default CherryPy dispatcher since the segments ``12`` and
569   ``15`` will not be matched to any Python callable.
570
571   This can be easily workaround with two handy CherryPy features
572   explained in the :ref:`advanced section <restful>`.
573
574
575
576.. _tut08:
577
578
579Tutorial 8: Make it smoother with Ajax
580######################################
581
582In the recent years, web applications have moved away from the
583simple pattern of "HTML forms + refresh the whole page". This
584traditional scheme still works very well but users have become used
585to web applications that don't refresh the entire page.
586Broadly speaking, web applications carry code performed
587client-side that can speak with the backend without having to
588refresh the whole page.
589
590This tutorial will involve a little more code this time around. First,
591let's see our CSS stylesheet located in `public/css/style.css`.
592
593.. code-block:: css
594   :linenos:
595
596   body {
597     background-color: blue;
598   }
599
600   #the-string {
601     display: none;
602   }
603
604We're adding a simple rule about the element that will display
605the generated string. By default, let's not show it up.
606Save the following HTML code into a file named `index.html`.
607
608.. code-block:: html
609   :linenos:
610
611   <!DOCTYPE html>
612   <html>
613     <head>
614       <link href="/static/css/style.css" rel="stylesheet">
615       <script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
616       <script type="text/javascript">
617         $(document).ready(function() {
618
619           $("#generate-string").click(function(e) {
620             $.post("/generator", {"length": $("input[name='length']").val()})
621              .done(function(string) {
622               $("#the-string").show();
623               $("#the-string input").val(string);
624             });
625             e.preventDefault();
626           });
627
628           $("#replace-string").click(function(e) {
629             $.ajax({
630               type: "PUT",
631               url: "/generator",
632               data: {"another_string": $("#the-string input").val()}
633             })
634             .done(function() {
635               alert("Replaced!");
636             });
637             e.preventDefault();
638           });
639
640           $("#delete-string").click(function(e) {
641             $.ajax({
642               type: "DELETE",
643               url: "/generator"
644             })
645             .done(function() {
646               $("#the-string").hide();
647             });
648             e.preventDefault();
649           });
650
651         });
652       </script>
653     </head>
654     <body>
655       <input type="text" value="8" name="length"/>
656       <button id="generate-string">Give it now!</button>
657       <div id="the-string">
658         <input type="text" />
659         <button id="replace-string">Replace</button>
660         <button id="delete-string">Delete it</button>
661       </div>
662     </body>
663   </html>
664
665We'll be using the `jQuery framework <http://jquery.com/>`_
666out of simplicity but feel free to replace it with your
667favourite tool. The page is composed of simple HTML elements
668to get user input and display the generated string. It also
669contains client-side code to talk to the backend API that
670actually performs the hard work.
671
672Finally, here's the application's code that serves the
673HTML page above and responds to requests to generate strings.
674Both are hosted by the same application server.
675
676.. code-block:: python
677   :linenos:
678
679   import os, os.path
680   import random
681   import string
682
683   import cherrypy
684
685
686   class StringGenerator(object):
687       @cherrypy.expose
688       def index(self):
689           return open('index.html')
690
691
692   @cherrypy.expose
693   class StringGeneratorWebService(object):
694
695       @cherrypy.tools.accept(media='text/plain')
696       def GET(self):
697           return cherrypy.session['mystring']
698
699       def POST(self, length=8):
700           some_string = ''.join(random.sample(string.hexdigits, int(length)))
701           cherrypy.session['mystring'] = some_string
702           return some_string
703
704       def PUT(self, another_string):
705           cherrypy.session['mystring'] = another_string
706
707       def DELETE(self):
708           cherrypy.session.pop('mystring', None)
709
710
711   if __name__ == '__main__':
712       conf = {
713           '/': {
714               'tools.sessions.on': True,
715               'tools.staticdir.root': os.path.abspath(os.getcwd())
716           },
717           '/generator': {
718               'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
719               'tools.response_headers.on': True,
720               'tools.response_headers.headers': [('Content-Type', 'text/plain')],
721           },
722           '/static': {
723               'tools.staticdir.on': True,
724               'tools.staticdir.dir': './public'
725           }
726       }
727       webapp = StringGenerator()
728       webapp.generator = StringGeneratorWebService()
729       cherrypy.quickstart(webapp, '/', conf)
730
731Save this into a file named `tut08.py` and run it as follows:
732
733.. code-block:: bash
734
735   $ python tut08.py
736
737Go to http://127.0.0.1:8080/ and play with the input and buttons
738to generate, replace or delete the strings. Notice how the page
739isn't refreshed, simply part of its content.
740
741Notice as well how your frontend converses with the backend using
742a straightfoward, yet clean, web service API. That same API
743could easily be used by non-HTML clients.
744
745.. _tut09:
746
747Tutorial 9: Data is all my life
748###############################
749
750Until now, all the generated strings were saved in the
751session, which by default is stored in the process memory. Though,
752you can persist sessions on disk or in a distributed memory store,
753this is not the right way of keeping your data on the long run.
754Sessions are there to identify your user and carry as little
755amount of data as necessary for the operation carried by the user.
756
757To store, persist and query data you need a proper database server.
758There exist many to choose from with various paradigm support:
759
760- relational: PostgreSQL, SQLite, MariaDB, Firebird
761- column-oriented: HBase, Cassandra
762- key-store: redis, memcached
763- document oriented: Couchdb, MongoDB
764- graph-oriented: neo4j
765
766Let's focus on the relational ones since they are the most common
767and probably what you will want to learn first.
768
769For the sake of reducing the number of dependencies for these
770tutorials, we will go for the :mod:`sqlite` database which
771is directly supported by Python.
772
773Our application will replace the storage of the generated
774string from the session to a SQLite database. The application
775will have the same HTML code as :ref:`tutorial 08 <tut08>`.
776So let's simply focus on the application code itself:
777
778.. code-block:: python
779   :linenos:
780
781   import os, os.path
782   import random
783   import sqlite3
784   import string
785   import time
786
787   import cherrypy
788
789   DB_STRING = "my.db"
790
791
792   class StringGenerator(object):
793       @cherrypy.expose
794       def index(self):
795           return open('index.html')
796
797
798   @cherrypy.expose
799   class StringGeneratorWebService(object):
800
801       @cherrypy.tools.accept(media='text/plain')
802       def GET(self):
803           with sqlite3.connect(DB_STRING) as c:
804               cherrypy.session['ts'] = time.time()
805               r = c.execute("SELECT value FROM user_string WHERE session_id=?",
806                             [cherrypy.session.id])
807               return r.fetchone()
808
809       def POST(self, length=8):
810           some_string = ''.join(random.sample(string.hexdigits, int(length)))
811           with sqlite3.connect(DB_STRING) as c:
812               cherrypy.session['ts'] = time.time()
813               c.execute("INSERT INTO user_string VALUES (?, ?)",
814                         [cherrypy.session.id, some_string])
815           return some_string
816
817       def PUT(self, another_string):
818           with sqlite3.connect(DB_STRING) as c:
819               cherrypy.session['ts'] = time.time()
820               c.execute("UPDATE user_string SET value=? WHERE session_id=?",
821                         [another_string, cherrypy.session.id])
822
823       def DELETE(self):
824           cherrypy.session.pop('ts', None)
825           with sqlite3.connect(DB_STRING) as c:
826               c.execute("DELETE FROM user_string WHERE session_id=?",
827                         [cherrypy.session.id])
828
829
830   def setup_database():
831       """
832       Create the `user_string` table in the database
833       on server startup
834       """
835       with sqlite3.connect(DB_STRING) as con:
836           con.execute("CREATE TABLE user_string (session_id, value)")
837
838
839   def cleanup_database():
840       """
841       Destroy the `user_string` table from the database
842       on server shutdown.
843       """
844       with sqlite3.connect(DB_STRING) as con:
845           con.execute("DROP TABLE user_string")
846
847
848   if __name__ == '__main__':
849       conf = {
850           '/': {
851               'tools.sessions.on': True,
852               'tools.staticdir.root': os.path.abspath(os.getcwd())
853           },
854           '/generator': {
855               'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
856               'tools.response_headers.on': True,
857               'tools.response_headers.headers': [('Content-Type', 'text/plain')],
858           },
859           '/static': {
860               'tools.staticdir.on': True,
861               'tools.staticdir.dir': './public'
862           }
863       }
864
865       cherrypy.engine.subscribe('start', setup_database)
866       cherrypy.engine.subscribe('stop', cleanup_database)
867
868       webapp = StringGenerator()
869       webapp.generator = StringGeneratorWebService()
870       cherrypy.quickstart(webapp, '/', conf)
871
872Save this into a file named `tut09.py` and run it as follows:
873
874.. code-block:: bash
875
876   $ python tut09.py
877
878Let's first see how we create two functions that create
879and destroy the table within our database. These functions
880are registered to the CherryPy's server on lines 85-86,
881so that they are called when the server starts and stops.
882
883Next, notice how we replaced all the session code with calls
884to the database. We use the session id to identify the
885user's string within our database. Since the session will go
886away after a while, it's probably not the right approach.
887A better idea would be to associate the user's login or
888more resilient unique identifier. For the sake of our
889demo, this should do.
890
891.. important::
892
893   In this example, we must still set the session to a dummy value
894   so that the session is not `discarded <https://cherrypy.readthedocs.org/en/latest/pkg/cherrypy.lib.html?highlight=fixation#session-fixation-protection>`_
895   on each request by CherryPy. Since we now use the database
896   to store the generated string, we simply store a dummy
897   timestamp inside the session.
898
899.. note::
900
901   Unfortunately, sqlite in Python forbids us
902   to share a connection between threads. Since CherryPy is a
903   multi-threaded server, this would be an issue. This is the
904   reason why we open and close a connection to the database
905   on each call. This is clearly not really production friendly,
906   and it is probably advisable to either use a more capable
907   database engine or a higher level library, such as
908   `SQLAlchemy <http://sqlalchemy.readthedocs.org>`_, to better
909   support your application's needs.
910
911.. _tut10:
912
913Tutorial 10: Make it a modern single-page application with React.js
914###################################################################
915
916In the recent years, client-side single-page applications (SPA) have
917gradually eaten server-side generated content web applications's lunch.
918
919This tutorial demonstrates how to integrate with
920`React.js <https://facebook.github.io/react/>`_, a Javascript library
921for SPA released by Facebook in 2013. Please refer to React.js
922documentation to learn more about it.
923
924To demonstrate it, let's use the code from :ref:`tutorial 09 <tut09>`.
925However, we will be replacing the HTML and Javascript code.
926
927First, let's see how our HTML code has changed:
928
929.. code-block:: html
930   :linenos:
931
932    <!DOCTYPE html>
933    <html>
934       <head>
935         <link href="/static/css/style.css" rel="stylesheet">
936         <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/react.js"></script>
937         <script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
938         <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
939       </head>
940       <body>
941         <div id="generator"></div>
942         <script type="text/babel" src="static/js/gen.js"></script>
943       </body>
944    </html>
945
946Basically, we have removed the entire Javascript code that was using jQuery.
947Instead, we load the React.js library as well as a new, local,
948Javascript module, named ``gen.js`` and located in the ``public/js``
949directory:
950
951.. code-block:: javascript
952   :linenos:
953
954   var StringGeneratorBox = React.createClass({
955     handleGenerate: function() {
956       var length = this.state.length;
957       this.setState(function() {
958         $.ajax({
959           url: this.props.url,
960           dataType: 'text',
961           type: 'POST',
962           data: {
963             "length": length
964           },
965           success: function(data) {
966             this.setState({
967               length: length,
968               string: data,
969               mode: "edit"
970             });
971           }.bind(this),
972           error: function(xhr, status, err) {
973             console.error(this.props.url,
974               status, err.toString()
975             );
976           }.bind(this)
977         });
978       });
979     },
980     handleEdit: function() {
981       var new_string = this.state.string;
982       this.setState(function() {
983         $.ajax({
984           url: this.props.url,
985           type: 'PUT',
986           data: {
987             "another_string": new_string
988           },
989           success: function() {
990             this.setState({
991               length: new_string.length,
992               string: new_string,
993               mode: "edit"
994             });
995           }.bind(this),
996           error: function(xhr, status, err) {
997             console.error(this.props.url,
998               status, err.toString()
999             );
1000           }.bind(this)
1001         });
1002       });
1003     },
1004     handleDelete: function() {
1005       this.setState(function() {
1006         $.ajax({
1007           url: this.props.url,
1008           type: 'DELETE',
1009           success: function() {
1010             this.setState({
1011               length: "8",
1012               string: "",
1013               mode: "create"
1014             });
1015           }.bind(this),
1016           error: function(xhr, status, err) {
1017             console.error(this.props.url,
1018               status, err.toString()
1019             );
1020           }.bind(this)
1021         });
1022       });
1023     },
1024     handleLengthChange: function(length) {
1025       this.setState({
1026         length: length,
1027         string: "",
1028         mode: "create"
1029       });
1030     },
1031     handleStringChange: function(new_string) {
1032       this.setState({
1033         length: new_string.length,
1034         string: new_string,
1035         mode: "edit"
1036       });
1037     },
1038     getInitialState: function() {
1039       return {
1040         length: "8",
1041         string: "",
1042         mode: "create"
1043       };
1044     },
1045     render: function() {
1046       return (
1047         <div className="stringGenBox">
1048               <StringGeneratorForm onCreateString={this.handleGenerate}
1049                                    onReplaceString={this.handleEdit}
1050                                    onDeleteString={this.handleDelete}
1051                                    onLengthChange={this.handleLengthChange}
1052                                    onStringChange={this.handleStringChange}
1053                                    mode={this.state.mode}
1054                                    length={this.state.length}
1055                                    string={this.state.string}/>
1056         </div>
1057       );
1058     }
1059   });
1060
1061   var StringGeneratorForm = React.createClass({
1062     handleCreate: function(e) {
1063       e.preventDefault();
1064       this.props.onCreateString();
1065     },
1066     handleReplace: function(e) {
1067       e.preventDefault();
1068       this.props.onReplaceString();
1069     },
1070     handleDelete: function(e) {
1071       e.preventDefault();
1072       this.props.onDeleteString();
1073     },
1074     handleLengthChange: function(e) {
1075       e.preventDefault();
1076       var length = React.findDOMNode(this.refs.length).value.trim();
1077       this.props.onLengthChange(length);
1078     },
1079     handleStringChange: function(e) {
1080       e.preventDefault();
1081       var string = React.findDOMNode(this.refs.string).value.trim();
1082       this.props.onStringChange(string);
1083     },
1084     render: function() {
1085       if (this.props.mode == "create") {
1086         return (
1087           <div>
1088              <input  type="text" ref="length" defaultValue="8" value={this.props.length} onChange={this.handleLengthChange} />
1089              <button onClick={this.handleCreate}>Give it now!</button>
1090           </div>
1091         );
1092       } else if (this.props.mode == "edit") {
1093         return (
1094           <div>
1095              <input type="text" ref="string" value={this.props.string} onChange={this.handleStringChange} />
1096              <button onClick={this.handleReplace}>Replace</button>
1097              <button onClick={this.handleDelete}>Delete it</button>
1098           </div>
1099         );
1100       }
1101
1102       return null;
1103     }
1104   });
1105
1106   React.render(
1107     <StringGeneratorBox url="/generator" />,
1108     document.getElementById('generator')
1109   );
1110
1111Wow! What a lot of code for something so simple, isn't it?
1112The entry point is the last few lines where we indicate that we
1113want to render the HTML code of the ``StringGeneratorBox`` React.js
1114class inside the ``generator`` div.
1115
1116When the page is rendered, so is that component. Notice how it
1117is also made of another component that renders the form itself.
1118
1119This might be a little over the top for such a simple example
1120but hopefully will get you started with React.js in the process.
1121
1122There is not much to say and, hopefully, the meaning of that code
1123is rather clear. The component has an internal `state <https://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html>`_
1124in which we store the current string as generated/modified by the user.
1125
1126When the user `changes the content of the input boxes <https://facebook.github.io/react/docs/forms.html>`_,
1127the state is updated on the client side. Then, when a button is clicked,
1128that state is sent out to the backend server using the API endpoint
1129and the appropriate action takes places. Then, the state is updated and so is the view.
1130
1131
1132Tutorial 11: Organize my code
1133#############################
1134
1135CherryPy comes with a powerful architecture
1136that helps you organizing your code in a way that should make
1137it easier to maintain and more flexible.
1138
1139Several mechanisms are at your disposal, this tutorial will focus
1140on the three main ones:
1141
1142- :ref:`dispatchers <dispatchers>`
1143- :ref:`tools <tools>`
1144- :ref:`plugins <busplugins>`
1145
1146In order to understand them, let's imagine you are at a superstore:
1147
1148- You have several tills and people queuing for each of them (those are your requests)
1149- You have various sections with food and other stuff (these are your data)
1150- Finally you have the superstore people and their daily tasks
1151  to make sure sections are always in order (this is your backend)
1152
1153In spite of being really simplistic, this is not far from how your
1154application behaves. CherryPy helps you structure your application
1155in a way that mirrors these high-level ideas.
1156
1157Dispatchers
1158^^^^^^^^^^^
1159
1160Coming back to the superstore example, it is likely that you will
1161want to perform operations based on the till:
1162
1163- Have a till for baskets with less than ten items
1164- Have a till for disabled people
1165- Have a till for pregnant women
1166- Have a till where you can only using the store card
1167
1168To support these use-cases, CherryPy provides a mechanism called
1169a :ref:`dispatcher <dispatchers>`. A dispatcher is executed early
1170during the request processing in order to determine which piece of
1171code of your application will handle the incoming request. Or, to
1172continue on the store analogy, a dispatcher will decide which
1173till to lead a customer to.
1174
1175Tools
1176^^^^^
1177
1178Let's assume your store has decided to operate a discount spree but,
1179only for a specific category of customers. CherryPy will deal
1180with such use case via a mechanism called a :ref:`tool <tools>`.
1181
1182A tool is a piece of code that runs on a per-request
1183basis in order to perform additional work. Usually a tool is a
1184simple Python function that is executed at a given point during
1185the process of the request by CherryPy.
1186
1187Plugins
1188^^^^^^^
1189
1190As we have seen, the store has a crew of people dedicated to manage
1191the stock and deal with any customers' expectation.
1192
1193In the CherryPy world, this translates into having functions
1194that run outside of any request life-cycle. These functions should
1195take care of background tasks, long lived connections (such as
1196those to a database for instance), etc.
1197
1198:ref:`Plugins <busplugins>` are called that way because
1199they work along with the CherryPy :ref:`engine <cpengine>`
1200and extend it with your operations.
1201