L’une des grandes plaies du génie logiciel est la réinvention perpétuelle de la roue. Récemment confronté à un problème de génération de code, j’ai décidé de briser le cercle infernal. Ce que je m’apprêtais à ne pas redévelopper pour la énième fois s’appelle “template engine”. Travaillant dans le monde .NET, j’ai donc juste googlé “template engine .net” et je suis tout de suite tombé sur StringTemplate. Ayant déjà eu un bon feeling avec le compilateur de compilateur ANTLR je n’ai pas hésité à investir un peu de temps pour explorer le package.
Les points forts de StringTemplate sont:
Stricte séparation modèle/vue (en gros, pas de template spaghetti comme JSP ou ASP).
Fondements théoriques clairs et argumentés (ST, le langage de définition des templates, présente toutes les caractéristiques d’un langage fonctionnel).
Modèle push (toutes les données sont fournies au template avant l’évaluation i.e. pas de connexion à une base de données au sein du template!)
Une syntaxe très clean aussi bien pour les templates que pour l’API.
Bonne pérennité et bon support (ANTLR existe depuis 1989).
Disponible pour Java, .NET et Python.
En m’inspirant des exemples fournis, j’ai pu très vite monter mon système. En fait StringTemplate permet de faire les choses simples très facilement et possède assez de puissance pour rendre les choses bien plus complexes faisables sans trop bidouiller.
Pour le petit job que j’avais à faire les concepts de base m’ont suffit:
Groupe de templates (classe StringTemplateGroup) qui permet de définir une espèce de CLASSPATH où les fichiers template sont stockés (avec une extension “.st”). Un template stocké peut ensuite être référencé par son nom ou son chemin. En gros un template peut être vu comme une fonction.
Attribut multi-valué: il s’agit d’un attribut qui contient une liste de valeurs vs. attribut simple.
Application d’un template qui s’apparente à un appel de fonction.
Le principe de StringTemplate est plus long à expliquer qu’à coder. Imaginons que l’on veuille réaliser un mailing à partir d’une liste de noms et d’adresses. Il suffit de créer un fichier template appelé “c:\templates\mailing.st” avec le contenu suivant:
Cher $nom$,
Nous savons que vous habitez dans la ville de $ville$.
Signé: Furax
Comme vous l’aurez deviné, les éléments du template sont isolés entre des dollars (”$”). Il est aussi possible d’utiliser les brackets à la HTML (”<” et “>”). Dans notre programme, le fichier mailing “mailing.txt” est généré grâce au code suivant:
using System;
using System.IO;
using Antlr.StringTemplate;
using Antlr.StringTemplate.Language;
StringTemplateGroup group =
new StringTemplateGroup("templates", @"c:\templates",
typeof(DefaultTemplateLexer));
StringTemplate mailing = group.GetInstanceOf("mailing");
mailing.SetAttribute("nom", "Jean Paul");
mailing.SetAttribute("ville", "Paris");
TextWriter tw = new StreamWriter("mailing.txt");
tw.WriteLine(mailing.ToString());
tw.Close();
Pour la petite histoire, j’ai bien apprécié la syntaxe de bloc inspirée de Smalltalk pour l’application de templates qui permet d’écrire des trucs du genre:
$records:{ record |
fields = new string[] {
$record.fieldNames:{ f | "$f$"}; separator=", "$ };
values = new object[] {
$record.fieldCValues; separator=", "$ };
}
Si records est une liste d’objets record dont l’attribut fieldNames contient une liste de noms de champs et l’attribut fieldCValues une liste d’expression C#, l’évaluation du template pourrait donner le résultat suivant:
fields = new string[] { "A", "B", "C" };
values = new object[] { 0, "32", DateTime.Today };
fields = new string[] { "A", "B", "C" };
values = new object[] { 44, "AA", DBNull.Value };
Je vais certainement réutiliser StringTemplate, notamment dans sa version pour Python. Par exemple, je pourrai l’utiliser avantageusemet comme une alternative gratuite à CodeSmith.