La dernière fois, je vous ai laissé avec pas grand chose, à savoir couchapp installé et une squelette d'application.
Aujourd'hui, nous allons être nettement plus ambitieux en prenant comme prétexte une application de gestion de favoris (à la delicious, en mode mono-utilisateur). Nous allons aborder le rendu d'un document en tant que tel (aka "show") et les listes de documents (aka "list").
Avant toute chose... couchapp !
Générons notre petite application :
couchapp-cloned mycouchapp tetram:CouchDB nicolas$ couchapp generate mycouchfav [INFO] Generating a new CouchApp in /Users/nicolas/Documents/Projets/CouchDB/mycouchfav
Et poussons notre application dans notre instance CouchDB (que vous avez démarré par ailleurs) :
tetram:CouchDB nicolas$ cd mycouchfav/ tetram:mycouchfav nicolas$ couchapp push myfav [INFO] Pushing CouchApp in /Users/nicolas/Documents/Projets/CouchDB/mycouchfav to design doc: http://127.0.0.1:5984/myfav/_design/mycouchfav [INFO] Visit your CouchApp here: http://127.0.0.1:5984/myfav/_design/mycouchfav/index.html
Vinrent ensuite les documents
Commençons par nous créer trois favoris depuis Futon ( http://localhost:5984/_utils/) avec la structure suivante :
- title
- url
- description
- tag
Ci-après mon jeu d'exemple au format json :
{ "_id": "couchdb", "_rev": "3-1834349775", "title": "CouchDB Official site", "url": "http://couchdb.apache.org/", "description": "Apache CouchDB is a distributed, fault-tolerant and schema-free document-oriented database accessible via a RESTful HTTP/JSON API", "tag": [ "couchdb", "database", "document" ] } { "_id": "couchdbkit", "_rev": "2-1087649587", "title": "Couchdbkit", "description": "Couchdbkit goal is to provide a framework for your Python application to access and manage Couchdb.", "url": "http://www.couchdbkit.org/", "tag": [ "couchdb", "python", "framework" ] } { "_id": "couchapp", "_rev": "2-3730993466", "title": "couchapp", "description": "Utilities to make standalone CouchDB application development simple", "url": "http://github.com/couchapp/couchapp/tree/master", "tag": [ "couchdb", "python", "ruby" ] }
Tu me le montres ton document ?
L'url pour accéder à un document est du type :
http://localhost:5984/<nom_de_la_db>/_design/<nom_du_design>/_show/<nom_du_show>/<id_du_document>
Dans notre cas, par ex : http://localhost:5984/myfav/_design/mycouchfav/_show/fav/couchdbkit (si je prends le favori ayant pour id "couchdbkit".
Vous venez de déduire que mon "show" se nomme "fav". Plus exactement, il s'agit du fichier show/fav.js avec le contenu suivant :
function(doc, req) { // !json templates.fav // !code vendor/couchapp/template.js // !code vendor/couchapp/path.js return template(templates.fav, { title : doc.title, url : doc.url, description : doc.description, assets : assetPath(), } ); }
Expliquons ce qu'il contient :
- Pour un show, la fonction js prend 2 arguments : doc et req ; ce dernier content la requête et va trouver le document associée à cette requête.
- On initialise une variable json, qui est utilisée ensuite pour appelé le template templates/fav.html
- On inclut ensuite 2 fichiers js fournis par couchapp
- Le return indique que l'on va charger le fichier templates/fav.html avec 4 variables qui sont le titre (title), l'url, la description du favori, ainsi qu'une variable assets qui permet de renseigner un chemin vers différents fichiers (css, js, etc).
Il nous resque plus qu'à construire le fichier templates/fav.html :
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
</head>
<body>
<div id="header"></div>
<div id="content">
<h1><a href="<%= url %>"><%= title %></a></h1>
<div class="body"><%= description %></div>
</div>
</body>
<script src="/_utils/script/json2.js"></script>
<script src="/_utils/script/jquery.js?1.2.6"></script>
<script src="/_utils/script/jquery.couch.js"></script>
<script src="<%= assets %>/vendor/couchapp/jquery.couchapp.js"></script>
</html>
Il ne nous reste plus qu'à .... et non pas visualiser votre page mais au préalable pousser vos fichiers dans votre instance CouchDB :
tetram:mycouchfav nicolas$ couchapp push myfav [INFO] Pushing CouchApp in /Users/nicolas/Documents/Projets/CouchDB/mycouchfav to design doc: http://127.0.0.1:5984/myfav/_design/mycouchfav [INFO] Visit your CouchApp here: http://127.0.0.1:5984/myfav/_design/mycouchfav/index.html
Maintenant, ouvrez votre navigateur sur l'url http://localhost:5984/myfav/_design/mycouchfav/_show/fav/couchdbkit et vous retrouvez le contenu très sommairement présenté de votre favori avec son titre, son url et sa description.
En conclusion, pour afficher un document, il nous faut :
- Un template (ici : templates/fav.html)
- Une fonction en javascript qui nous permet de manipuler les attributs du documents en vue de leur rendu. La fonction est de type function(doc,req). La fonction est située dans un fichier shows/nom_du_show.js (ici shows/fav.js)
- Ne pas oublier de faire un "couchapp push" pour envoyer vos fichiers dans votre instance CouchDB
- Une url du type : http://localhost:5984/<nom_de_la_db>/_design/<nom_du_design>/_show/<nom_du_show>/<id_du_document>
Et ta liste, tu me la montres aussi ?
Pour les listes, c'est un peu plus complexe mais pas insurmontable 
L'url est du type :
http://localhost:5984/<nom_de_la_db>/_design/<nom_du_design>/_list/<nom_de_la_liste>/<nom_de_la_vue>?<paramètres>
Supposons que je veuille créer la page d'accueil de mon application, celle-ci listant tous les favoris stockés. Le nom de la liste pourrait donc tout naturellement s'appeller "all" et le nom de la vue "favs". On pourrait imaginer avoir une liste "search" par ex pour afficher les résultats d'une recherche sur les favoris avec des paramètres de recherche.
Il faut donc bien avoir en tête qu'une liste est un moyen d'afficher une vue. Une même vue peut donc partager plusieurs listes.
Ex :
http://localhost:5984/myfav/_design/mycouchfav/_list/all/favs http://localhost:5984/myfav/_design/mycouchfav/_list/search/favs?limit=25&key=%22python%22
Commençons donc par créer notre vue, avec le fichier views/favs/map.js
function(doc) { emit(doc._id, {title: doc.title, description: doc.description, url:doc.url }) };
Cette fonction me renvoi tous les documents de mon instance, trié par "_id" et avec une série de variables (title, description, url - je ne les réexplique pas, ce sont les mêmes que précédemment).
Passons à notre liste : lists/all.js
function(head, row, req, info) { // !json templates.all // !code vendor/couchapp/path.js // !code vendor/couchapp/template.js return respondWith(req, { html : function() { if (head) { return template(templates.all.head, { assets : assetPath(), }); } else if (row) { return template(templates.all.row, { fav: row.value, }); } else { return template(templates.all.tail, { assets : assetPath(), }); } }, }) };
La nouveauté ici tient dans la fonction qui prend différents paramètres en compte (contenus honteusement récupérés de benoitc "Légo 2 : les listes"):
- head: est renseigné lorsque couchdb envoie la tête de la vue. head est un objet contenant les paramètres "total-rows" et "offset" correspondant au résultat de la vue. Cela nous permet, par exemple comme dans l'exemple ci-dessus, afficher l'entête d'une page html en chargeant un template particulier.
- row : la ligne de la vue actuellement envoyée par CouchDB.
- req : un objet contenant les paramètres de la requête
- row_info : un objet contenant les infos sur la ligne en cours, sa position, quel était la ligne précendente, quelle est la première ligne de la vue.
L'autre nouveauté tient dans la gestion des templates HTML :
- Pour head, on va charger templates/all/head.html
- Pour chaque ligne de résultat, on va charger templates/all/row.html
- Une fois que les résultats de la requête sont épuisés, on va charger templates/all/tail.html (qui fait donc office de voiture-balai).
Contenu de templates/all/head.html :
<!DOCTYPE html>
<html>
<head>
<title>My favs</title>
<link rel="stylesheet" href="../../screen.css" type="text/css"/>
</head>
<body>
<div id="header">
<h2>My favs...</h2>
</div>
<div id="content">
<h1>Recently...</h1>
<dl id="bookmarks">
Contenu de templates/all/row.html :
<dt><a href="<%= fav.url %>"><%= fav.title %></a></dt> <dd><%= fav.description %></dd>
Contenu de templates/all/tail.html :
</dl>
</div>
</body>
<script src="/_utils/script/json2.js"></script>
<script src="/_utils/script/jquery.js"></script>
<script src="/_utils/script/jquery.couch.js"></script>
<script src="<%= assets %>/vendor/couchapp/jquery.couchapp.js"></script>
</html>
Il ne vous reste plus qu'à pousser vos modifications dans votre instane CouchDB (ici en mode verbeux pour bien voir ce qui est poussé) :
tetram:mycouchfav nicolas$ couchapp push -v myfav [INFO] push lists/all.js [INFO] push shows/fav.js [INFO] push templates/all/head.html [INFO] push templates/all/row.html [INFO] push templates/all/tail.html [INFO] push templates/fav.html [INFO] push vendor/couchapp/_attachments/jquery.couchapp.js [INFO] push vendor/couchapp/couchapp.js [INFO] push vendor/couchapp/date.js [INFO] push vendor/couchapp/path.js [INFO] push vendor/couchapp/README.md [INFO] push vendor/couchapp/template.js [INFO] push views/favs/map.js [INFO] process code macro: /Users/nicolas/Documents/Projets/CouchDB/mycouchfav/vendor/couchapp/template.js [INFO] process code macro: /Users/nicolas/Documents/Projets/CouchDB/mycouchfav/vendor/couchapp/path.js [INFO] process code macro: /Users/nicolas/Documents/Projets/CouchDB/mycouchfav/vendor/couchapp/path.js [INFO] process code macro: /Users/nicolas/Documents/Projets/CouchDB/mycouchfav/vendor/couchapp/template.js [INFO] Pushing CouchApp in /Users/nicolas/Documents/Projets/CouchDB/mycouchfav to design doc: http://127.0.0.1:5984/myfav/_design/mycouchfav [INFO] Visit your CouchApp here: http://127.0.0.1:5984/myfav/_design/mycouchfav/index.html
Rendez vous ensuite sur : http://localhost:5984/myfav/_design/mycouchfav/_list/all/favs
Au bout du compte, nous venons de voir pour les listes :
- Les listes comme moyen d'afficher une vue,
- Cela se traduit par un fichier de vue (views/mon_de_ma_vue/map.js) et un fichier liste (lists/<nom_de_ma_liste>.js)
- La liste est une fonction de type function(head, row, req, info)
- On peut spliter les templates en head.html / row.html / tail.html
- url de type : http://localhost:5984/<nom_de_la_db>/_design/<nom_du_design>/_list/<nom_de_la_liste>/<nom_de_la_vue>?<paramètres>
Au bout de cette "journée"
Nous avons vu aux termes des parties 1 et 2 :
- Comment installer couchapp et générer le squelette d'une application Couchdb
- Jouer un peu avec Futon pour créer des enregistrements dans CouchDB (même si je ne le couvre pas directement, voir pas du tout en fait)
- Comment afficher des documents sous forme de "list/views" pour une collection de documents et de "show" pour un document de façon unitaire
Il reste encore plein de choses à voir (au moins à mon niveau) :
- Voir comment on peut remplacer le "My favs ... " dans head.html par une variable extérieure au template (facile - déjà fait mais cela rajoutait une grande quantité d"infos à ce post déjà très long je pense...)
- Mieux comprendre les "!json" et "!code"
- Les options de tri
- <insérer votre sujet ici via un commentaire>
- ...
Sources utiles
Les lectures indispensables pour faire ce tutoriel :
- benoitc : "Légo 2 : les listes"
- benoitc : "Légo 3 : Tu as vu comme mon doc rend bien ?"
- CouchDB Wiki : "Formatting with Show and List"
Au passage, les nouveautés de CouchDB / Couchapp mentionnées pat Benoit sont des plus intéressantes (surtout pour les listes, plus souple/flexible a priori).
Merci à benoitc pour éclairer ma lanterne sur couchdb/couchapp ces derniers jours 


