Afficher des documents dans des formats particuliers

Les fonctions d’affichage de CouchDB forment un API RESTful qui trouve son inspiration dans une fonction similaire offerte par Lotus Notes. En bref, elles vous permettent de fournir le document à vos clients dans le format que vous désirez.

Une fonction d’affichage forge une réponse HTTP avec n’importe quel Content-Type et basée sur un document JSON stocké. Dans le cadre de Sofa, nous les utiliserons pour afficher les articles du blogue à partir de leur lien permanent. De cette manière, nous garantissons que ces pages pourront être indexées par les moteurs de recherche et plus facilement accessibles. La fonction d’affichage de Sofa affiche chaque article dans une page HTML qui lui est propre, avec des liens vers les feuilles de style et autres ressources, lesquelles sont des pièces jointes du design document.

Et voilà ! Fantastique ! Nous venons d’afficher un article du blogue ! Voyez vous-même la figure 1, Un article affiché.

Figure 1. Un article affiché

La fonction d’affichage combinée à son patron (template en anglais) génèrera une page statique susceptible d’être conservée par les mécanismes d’antémémoire (en anglais, cacheable) et qui ne dépendra aucunement du contexte de la session. Elle se basera uniquement sur le document et le Content-Type demandés. La génération de code HTML dans la fonction d’affichage n’affecte pas le contenu de la base de données, ce qui en fait un atout pour la conception d’applications simples et résistantes au passage à l’échelle.

Utiliser les fonctions d’affichage

Examinons le code source. Le premier élément sur lequel nous nous arrêtons est le corps de la fonction JavaScript, lequel est très simple : il appelle une fonction de patronage pour générer la page HTML. Entrons tout de même dans les détails :

function(doc, req) {
  // !json templates.post
  // !json blog
  // !code vendor/couchapp/template.js
  // !code vendor/couchapp/path.js

Nous reconnaissons les macros !code et !json abordées dans le chapitre 12, Stockage des documents. Présentement, elles servent à importer un patron et quelques métadonnées décrivant le blogue (sous forme de données JSON). Elles importent aussi les liens et les fonctions de transcription.

Ensuite, nous exploitons le patron :

  return template(templates.post, {
    title : doc.title,
    blogName : blog.title,
    post : doc.html,
    date : doc.created_at,
    author : doc.author,

L’intitulé de l’article, le corps HTML, l’auteur et la date de publication sont extraits du document conjointement avec le nom du blogue. Les trois instructions suivantes exploitent la bibliothèque path.js pour générer des liens en fonction du chemin indiqué dans la requête. Cela garantit qu’ils désigneront une ressource existante au sein de l’application.

    assets : assetPath(),
    editPostPath : showPath('edit', doc._id),
    index : listPath('index','recent-posts',{descending:true, limit:5})
  });
}

Nous avons donc vu que le corps de la fonction, en lui-même, ne fait que calculer quelques valeurs (basées sur le document, la requête et quelques données spécifiques au déploiement tel que le nom de la base de données) pour les communiquer à une fonction chargée de les injecter dans le patron. Par conséquent, le cœur de l’action se trouve dans le patron HTML que nous nous proposons d’examiner.

Le patron de la page de présentation d’un article

Le patron définit le format HTML publié, à l’exception de quelques tags qui sont substitués par du contenu dynamique. Dans le cas de Sofa, ces éléments dynamiques ressemblent à <%= replace_me %>, ce qui correspond à un délimiteur de tag classique.

Le moteur de rendu de patron utilisé par Sofa a été adapté à partir de l’article de blogue de John Resig intitulé JavaScript Micro-Templating. Il a été sélectionné car c’est le plus moteur le plus simple qui fonctionnait sans modification en étant exploité côté serveur. Recourir à un autre moteur de rendu serait un simple exercice.

Examinons la chaîne de caractères composant le patron. Rappelez-vous qu’il est inclus dans le code JavaScript par la macro CouchApp !json et que CouchApp s’occupe de le préparer en vue de son utilisation par le moteur (notamment en échappant les caractères spéciaux).

<!DOCTYPE html>
<html>
  <head>
    <title><%= title %> : <%= blogName %></title>

C’est la première fois que nous observons un tag dans son contexte. Le titre de l’article ainsi que le nom du blogue (tel que défini dans blog.json) sont ici utilisés pour forger le tag HTML <title>.

    <link rel="stylesheet" href="../../screen.css" type="text/css">

Puisque les fonctions d’affichage sont appelées par le design document, elles bénéficient de son contexte, ce qui permet de pointer vers les pièces jointes avec des URI relatifs. Ici, nous faisons référence à screen.css, lequel fichier se trouve dans le répertoire _attachments à la racine du dossier contenant le code source de Sofa.

  </head>
  <body>
    <div id="header">
      <a id="edit" href="<%= editPostPath %>">Edit this post</a>
      <h2><a href="<%= index %>"><%= blogName %></a></h2>

De nouveau, des tags sont destinés à être remplacés par le contenu dynamique correspondant. Dans ce cas, nous pointons vers la page d’édition de l’article courant ainsi que vers la page d’accueil du blogue.

    </div>
    <div id="content">
      <h1><%= title %></h1>
      <div id="post">
        <span class="date"><%= date %></span>

L’intitulé de l’article est utilisé pour le tag <h1> et la date est insérée dans un tag particulier de classe date. Référez-vous à la section États dynamiques pour une explication sur la raison qui nous pousse à écrire une date statique en lieu et place d’un format plus lisible par l’utilisateur comme, par exemple, « il y a trois jours ».

        <div class="body"><%= post %></div>
      </div>
    </div>
  </body>
</html>

Dans la fermeture du patron, nous transcrivons le contenu de l’article au format HTML (rappelez-vous, c’est le fruit de la traduction du format Markdown et il fut sauvegardé à partir du navigateur de l’auteur).

États dynamiques

Lorsque CouchDB est abrité par un serveur mandataire assurant une fonction d’antémémoire (caching proxy en anglais), chaque fonction d’affichage ne devrait être appelée qu’une seule fois par mise à jour du document. Cela explique notamment notre préférence pour la date 2008/12/25 23:27:17 +0000 au lieu d’« il y a neuf jours ».

Cela signifie aussi que pour les éléments de présentation qui dépendent de la date actuelle, ou de l’identité de la personne qui navigue sur la page, nous aurons besoin de nous appuyer sur du code JavaScript côté client afin d’apporter les modifications de manière dynamique.

    $('.date').each(function() {
      $(this).text(app.prettyDate(this.innerHTML));
    });

Nous insérons ici ce détail de l’implémentation JavaScript du côté du navigateur non pour parler d’Ajax, mais pour démontrer l’approche la plus sensée dans le cadre de la présentation d’un document à une application client. En effet, CouchDB devrait fournir le format le plus utile pour exploiter le document, tel que le client le demande. C’est ainsi que vous épargnez des cycles processeurs et de la mémoire sur le serveur : en laissant le soin des dernières retouches de style au navigateur client. Cela s’avère tout particulièrement utile quand vous devez intégrer des données en provenance d’autres requêtes ou quand vous voulez afficher des informations synchronisées avec d’autres services web. Puisqu’il y a normalement un plus grand nombre de clients que de serveurs CouchDB, ce déport de charge vous permet de satisfaire un plus grand nombre de clients avec la même infrastructure.