Article publié dans Linux Magazine 92, mars 2007.
Copyright © 2007 - Damien Krotkine.
$r
et $m
: Deux couteaux suisses
Mason est un framework, basé sur mod_perl, qui permet de créer des sites web dynamiques. Puissant et polyvalent, son succès est grandissant. Utilisable aussi bien de manière autonome qu'encapsulé dans un autre framework, son terrain de jeu est très vaste.
Cet article présente la mise en œuvre de Mason dans un environnement classique Apache/mod_perl. Il sera suivi d'un second article traitant les concepts avancés, et enfin d'une étude de cas détaillée, avec code source complet.
Perl Mason est un framework complet qui permet de « créer, servir et gérer des sites web ». Créé en 1998 et développé principalement par Jonathan Swartz et Dave Rolsky, Mason est plutôt facile à apprendre et polyvalent. Il peut se comparer à PHP pour sa facilité d'utilisation, à JSP pour son design moderne. Et surtout, Mason permet d'utiliser toute la puissance de Perl depuis un environnement totalement orienté web.
Une définition simple de Mason peut être : « un ensemble de modules pour dynamiser du contenu statique ». Cette description est très vague, car Mason peut générer n'importe quel type de contenu, même si on l'utilise principalement pour du HTML. Mason peut être appelé depuis un programme Perl autonome, ou bien à partir d'un CGI, voire être appelé depuis un framework de complexité plus grande, pour répondre aux besoins de mises en forme. Ainsi, Mason est une option parmi les différents modules de template de Catalyst, un MVC en Perl. Jifty [1] est un autre exemple de framework web prometteur basé sur Mason.
MVC (Modèle Vue Contrôleur)
C'est une méthode de conception d'applications, plutôt ancienne (1979) qui a été mise au goût du jour par le succès de frameworks web développés suivant ce principe. Cette architecture instaure une séparation entre les données (Modèles), la présentation (Vues), et le traitement des événements (Contrôleurs).
Mason est utilisé principalement dans le cadre de programmation web orientée page. Il est généralement mis en œuvre par dessus le couple Apache/mod_perl. Ses atouts sont une syntaxe puissante, un jeu de modules proposant beaucoup de fonctionnalités, et la possibilité d'utiliser facilement des modules Perl tiers. Il peut également représenter une page en utilisant le modèle objet. Mason peut être vu comme le PHP de Perl, mais ne se limite pas à cela.
Commençons par l'inévitable hello world. Voici une page HTML enrichie grâce à la syntaxe de Mason :
<html> <body> % my $variable = 'World'; Hello <% $variable %>! <%perl> use POSIX qw(uname); my $systeme = (uname())[0]; </%perl> Ce serveur web utilise <% $systeme %>. </body> </html>
Après un examen rapide de ces quelques lignes, on peut en déduire les règles de base :
Le texte simple (ou HTML, XHTML, etc) sera transmis tel quel en réponse.
Le caractère %
en début de ligne permet d'évaluer du Perl
facilement. Le reste de la ligne est considéré comme du Perl. Toutes les
lignes de Perl d'un même fichier sont considérées comme étant dans
la même portée lexicale. Attention, le caractère pourcent %
doit vraiment
être en début de ligne, il ne peut pas être précédé
d'espaces.
Lorsque le code Perl à exécuter dépasse une ligne, il est plus
avantageux d'utiliser <%perl>
et </%perl>
. Ces balises permettent
d'encapsuler des portions de Perl sur plusieurs lignes.
La balise <% %>
renvoie le résultat de l'évaluation du code Perl
qu'elle contient. Elle est généralement utilisée pour afficher du
contenu qui a été calculé précédemment dans une portion de code
entre <%perl>
et </%perl>
, ou issu d'une ligne commençant par
%
.
Cet exemple n'est qu'un petit extrait de ce qu'il est possible de faire avec
Mason, mais illustre la philosophie globale du framework : être accessible et
facile d'utilisation, tout en restant concis. Ceci est possible grâce à
l'utilisation de Perl directement dans la page web, contrairement à
d'autres syntaxes, par exemple Template::Toolkit
, qui nécessitent
l'apprentissage d'un nouveau langage.
Voici quelques structures de code qu'on rencontre couramment dans une page Mason :
Voici les différents types de commentaires :
%# ceci est un commentaire <% # Ceci est également un commentaire, rarement utilisé %>
Attention, la première syntaxe requiert que le signe %
soit le premier
caractère de la ligne.
Afficher du contenu HTML de manière conditionnelle :
% if ($produit->get_stock() <= 0) { <span class='alert'>Produit indisponible</span> % }
Cela permet de mélanger perl et HTML. Dans ce cas, La page affiche « Produit indisponible » uniquement si le produit n'est plus en stock.
Au même titre qu'un test conditionnel, il est facile d'utiliser for
,
foreach
, while
et autres <unless
:>
<ul> % foreach my $attribut ($produit->get_attributs) { % my $cle = $attribut->{cle} % my $valeur = $attribut->{valeur} <li> <% $cle %> : <% $valeur %> </li> % } </ul>
Appeler un module Perl dans une page web est très simple, voici un exemple
de code qui vérifie si le drapeau utf8
est vrai pour une variable :
<%perl> use Encode; my $chaine = "Exemple de chaîne de caractère avec accents"; my $drapeau_utf8 = Encode::is_utf8(); </%perl> % if ($drapeau_utf8) { <span class='info'>La représentation interne de la chaîne "<% $chaine %>" est en UTF8</span> % } else { <span class='info'>La représentation interne de la chaîne "<% $chaine %>" est en latin1</span> % }
Un autre exemple typique est d'utiliser Data::Dumper
pour afficher le contenu
d'une variable. Dans le code suivant, nous appelons la méthode Dumper()
sur
une variable $a
, mais au passage nous remplaçons les retours à la
ligne par des retours chariots HTML.
% use Data::Dumper; % my $a = { foo => 'bar' } <% join("<br>\n", split("\n", Dumper(\$a))) %>
Un site Web basé sur Mason repose en général sur trois types de fichiers :
Il s'agit de pages de HTML (ou XHTML), pouvant contenir des balises Mason. Ces balises doivent être utilisées pour la forme (afficher un résultat de requête, du contenu), mais pas pour le fond (le calcul d'un résultat par exemple). Ces pages peuvent également être du HTML pur. On stocke généralement ces pages dans des fichiers en suffixe .html.
Ces pages ne contiennent que des instructions Mason. Comme nous le verrons plus
tard, il est possible de créer des composants et des méthodes. Il arrive qu'on ait
besoin d'effectuer des tâches dans l'environnement Mason, en utilisant les
fonctionnalités des objets « request » de mod_perl $r
et Mason $m
(nous
verrons ces deux objets plus bas). Ces pages ne doivent pas générer de contenu
HTML sur la sortie standard, mais uniquement une valeur de retour. Ces fichiers
ont souvent pour suffixe .mhtml ou .mason.
L'intelligence du site doit être placée dans des modules Perl, qui seront appelés depuis les pages HTML ou les pages Mason. L'entrée et la sortie de ces modules doivent être bien définies. Ils ne doivent pas créer de HTML directement, mais générer des structures de données, qui seront mises en forme plus tard. Tout code qui n'a pas besoin d'utiliser l'environnement Mason ou mod_perl doit être placé dans un module Perl, qui comme le veut la convention, est un fichier qui se termine par .pm.
Séparer les rôles de chaque page par type permet un développement plus structuré. Bien sûr ce n'est qu'une convention, il faut donc avoir la volonté de la suivre. Cependant cela fait partie des Bonnes Pratiques du développeur Mason.
Mason est un module Perl, dont le nom exact est HTML::Mason
[2]. Il peut être
utilisé en mode autonome, en CGI, ou via mod_perl. Nous nous concentrerons
sur l'utilisation de Mason avec le couple Apache/mod_perl.
Pour commencer, il nous faut un Apache avec mod_perl. Les exemples présentés ici utilisent Apache 1.x et mod_perl 1.x. Mason est compatible mod_perl 2 à partir de la version 1.29_02. Bien sûr, cela ne change rien à son utilisation. La plupart des distributions Linux permettent d'installer facilement le couple Apache/mod_perl. Une majorité d'entre elles proposent des paquetages Mason récents, mais si ce n'est pas le cas, il peut être installé à partir de CPAN.
Voyons rapidement la configuration du serveur Web. Ces lignes sont à rajouter dans le fichier de configuration d'Apache, par exemple /etc/apache/httpd.conf :
PerlModule HTML::Mason::ApacheHandler <Location /> SetHandler perl-script PerlHandler HTML::Mason::ApacheHandler PerlSetVar MasonDataDir /var/cache/apache </Location>
Ceci permet de faire passer toutes les pages du site Web par l'interpréteur Mason. Celui-ci va analyser le contenu, exécuter les commandes spéciales et produire une page résultat.
On notera également la ligne
PerlSetVar MasonDataDir /var/cache/apache
« Code Cache »
Pour des raisons de performances, Mason précompile les pages à servir. Les objets précompilés sont stockés sur le disque dur, dans ce qu'on appelle le « Code Cache ». Cela n'a rien à voir avec le système de cache utilisable par le développeur, que nous verrons dans le prochain article.
Elle permet d'indiquer à Mason où stocker le contenu du « Code Cache ». Nous la spécifions ici car c'est généralement la première chose qui bloque l'installation rapide de Mason, et la création d'une première page. En effet, ce répertoire doit être accessible en écriture par l'utilisateur système à qui appartient le processus Apache.
Ainsi configuré, Apache doit pouvoir servir des pages web Mason. Généralement,
le serveur web affiche une page par défaut pour signifier qu'il fonctionne
bien, située par exemple dans /var/www/localhost/htdocs/index.html. Nous
pouvons tester si Mason fonctionne bien en introduisant une de ses balises dans
ce fichier. <% 6 * 7 %>
fera l'affaire. Si 42 apparaît lorsque la
page est chargée, alors cela fonctionne correctement.
La configuration d'Apache que nous avons utilisée est un peu simpliste, voire franchement mauvaise, car tous les types de contenu seront interprétés. Cela pose un problème, car les images et autres fichiers binaires seront analysés. La performance s'en trouvera dégradée, et pour peu que leur interprétation en ASCII contienne des balises Mason, le site web sera mal rendu. Filtrons donc l'application de l'analyseur syntaxique sur les suffixes qui nous intéressent :
PerlModule HTML::Mason::ApacheHandler <LocationMatch "\.html$"> SetHandler perl-script PerlSetVar MasonDataDir /var/cache/apache PerlHandler HTML::Mason::ApacheHandler </LocationMatch> <LocationMatch "(\.mhtml|dhandler|autohandler)$"> SetHandler perl-script PerlInitHandler Apache::Constants::NOT_FOUND </LocationMatch>
Nous indiquons à Apache qu'il doit servir les pages au suffixe .html via Mason, et renvoyer un 404 pour les .mhtml, mais également pour les dhandler et autohandler (voir plus bas), car nous ne voulons pas qu'un internaute puisse voir leur contenu.
Quand faut-il redémarrer Apache ? Mason est intelligent, il précompile les
pages à servir, et se rend compte lorsqu'elles ont été modifiées. Ainsi il
n'est pas nécessaire de redémarrer Apache lorsque l'on modifie une page HTML +
Mason ou Mason pure. Par contre, si votre site web utilise des modules Perl, il
faudra redémarrer Apache à chaque changement dans ces modules
(ou utiliser Apache::Reload
).
Paramétrer Mason peut se faire depuis le fichier de configuration d'Apache,
ou bien depuis du code Perl. Une règle de nommage est à connaître :
en Perl, la notation est l'utilisation de minuscules et d'espaces soulignés
(underscore, _
). Quant au paramètre Apache, il s'écrit avec le
préfixe Mason et avec des majuscules au début de chaque mot. Ainsi, on
aura parametre_en_perl
et MasonParametreApache
.
Voici un exemple d'utilisation dans httpd.conf :
PerlSetVar MasonSessionClass Apache::Session::File
Cette ligne spécifie la classe à utiliser pour stocker les sessions.
Quelques paramètres souvent utilisés :
allow_globals
(MasonAllowGlobals
)
permet de spécifier une liste de variables globales à tous les composants.
comp_root
(MasonCompRoot
)
permet de spécifier la racine de la structure des composants, et comment les
chemins des composants sont traduits en chemins de fichiers. Dans l'utilisation
classique de Mason avec Apache ou en CGI, la valeur par défaut de comp_root
est
celle du DocumentRoot
. Cependant vous pouvez le changer. Vérifiez bien que
l'utilisateur sous lequel est lancé Apache a bien accès à ce répertoire. Il est
également possible de spécifier plusieurs racines.
data_dir
(MasonDataDir
)
est un paramètre important, qu'il est généralement nécessaire de configurer.
Il spécifie l'endroit où Mason va stocker diverses données utilisées pour
l'optimisation de l'exécution. L'utilisateur sous lequel Apache est lancé doit
y avoir accès en lecture/écriture. Le contenu de ce répertoire peut être
effacé, Mason ne l'utilise que pour des questions de performances. Sa valeur
par défaut est dépendante de votre distribution.
Une fois configuré, il ne reste plus qu'à démarrer le serveur Apache, et placer un fichier de test à la racine du site web. Par exemple, text.html qui contient :
<html><body> Test : <% 40 + 2 %> </body></html>
En ouvrant un navigateur web sur cette page, le nombre 42 devrait apparaître.
Un composant est un morceau de site web. Un site web est généralement constitué de plusieurs pages. Ces pages sont construites à partir de plusieurs éléments : par exemple un menu, un bandeau supérieur, un bandeau inférieur, un cadre de contenu, un encart de navigation, etc. Les composants Mason permettent d'implémenter ces éléments, et de les assembler. Prenons en exemple un petit site personnel. Considérons que chaque page est structurée comme suit :
un bandeau supérieur, contenant un logo et la date du jour
un menu de navigation vers les autres pages
le contenu de la page proprement dit
un bandeau inférieur, contenant des mentions légales
Ce site web contient plusieurs pages :
une page d'accueil
une page de brèves
une page « à propos de l'auteur »
Nous pouvons structurer le site web comme suit : un composant pour chaque page, lui même constitué de composants pour les bandeaux supérieur et inférieur, la navigation et le contenu.
Un composant est un fichier. Il n'a pas besoin de déclaration. Un fichier test.html contenant du HTML et du Mason est un composant sans le savoir.
Lors d'une requête sur le site web, Mason doit décider quel composant
(appelé « top-level ») va être interprété en premier. Il
est choisi en fonction de l'url. Par exemple, de l'adresse
http://www.siteweb.com/produits/cuillere.html?modele=7
Mason déduit qu'il lui faut appeler le composant cuillere.html, dans le
répertoire produits/. S'il n'existe pas (si le fichier /produits/cuillere.html
est inconnu), alors Apache renverra une erreur 404. En fait, nous verrons plus
tard qu'il est possible d'intercepter ce 404.
Nous venons de le voir, un composant peut s'appeler simplement en entrant son URL. Mais les composants peuvent bien sûr s'appeler entre eux. On utilise cette syntaxe :
<& chemin/du/composant.html, arguments_optionels &>
Le composant appelé sera interprété, et le contenu HTML résultant inséré en lieu et place de l'appel. Le chemin du composant suit la syntaxe des URI, donc le séparateur est /, et il peut être absolu ou relatif. Le répertoire courant est celui du composant appelant.
Voici le composant de la page d'accueil. Nous allons le créer dans index.html, ça sera la page par défaut de notre site web d'exemple :
%# index.html %# Début du composant index %# on appelle le bandeau supérieur <& bandeau_sup.html &> %# on appelle le menu de navigation %# on passe le nom de la page actuelle <& navigation.html, page_courante => 'index' &> %# Voici le contenu spécifique de la page d'accueil <center>Bonjour et bienvenue sur ce site web !</center> %# le bandeau inférieur <& bandeau_inf.html &>
Créons maintenant le composant du bandeau supérieur qui s'appelle bandeau_sup
,
qui va contenir les balises de début de page, et un logo. Editons donc le
fichier bandeau_sup.html :
%# bandeau_sup.html <html> <head></head> <body> <center><img src="/images/logo.png"></center>
C'est plutôt simple. Bien sûr, ce HTML n'est qu'un exemple, il n'est pas du tout conforme aux normes W3C. Voyons maintenant le composant de navigation. Le menu de navigation doit présenter un lien vers les autres pages du site web, mais pas vers celle affichée actuellement, car c'est redondant. Ainsi, sur la page d'accueil, le menu de navigation permet de se rendre sur la « page de brèves », ou vers l'« à-propos ». Sur la page « à-propos », le menu de navigation propose les liens vers la « page d'accueil » ou la « page de brèves». C'est pourquoi nous passons en argument le nom du lien à ne pas afficher (voir plus bas en détail le passage d'arguments). Voici le composant navigation :
%# navigation.html %# récupération de l'argument <%args> $page_courante </%args> %# on commence une table <table> %# on boucle sur tous les liens % foreach(qw(index breves apropos)) { %# si le lien n'est pas la $page_courante, on l'affiche % if ($_ ne $page_courante) { %# on affiche le HTML du lien, construit à la volée, dans une ligne de la table <tr><td> <a href="<% $_ . 'html' %>">$_</a> </td></tr> % } % } %# on ferme la table </table>
Et pour finir voici le source du bandeau inférieur qui ferme la page web :
%# bandeau_inf.html </body> </html>
Le fichier index.html est chargé par défaut lorsque l'utilisateur navigue sur
le site web. Il est cependant également possible d'appeler les autres pages
(donc composants) du site. Ainsi l'URL http://www.siteweb.com/bandeau_sup.html
permet d'appeler le composant bandeau_sup.html.
Voici quelques précisions sur le passage d'arguments d'un composant à un autre. Les arguments peuvent être de tout type : scalaire (nombre ou chaîne de caractères), tableau, ou hash. Ils peuvent être obligatoires ou optionnels. Voici un petit code d'exemple de passage d'arguments. Le prototypage est faite dans le composant appelé :
<%args> $a @b # des arguments obligatoires, de type scalaire, tableau ou hash %c $d => 5 # voici un argument scalaire optionnel, et sa valeur par défaut $e => $d * 2 @f => ('foo', 'bar') %g => (joe => 1, bob => 2) $h => undef # optionnel, par défaut undef </%args>
Et @_
?
Si vous n'aimez pas le passage de paramètres nommés, Il est toujours possible d'utiliser @_
:
# côté appelant <& composant, 42, 'chaîne' &> # côté appelé my ($reponse, $chaine) = @_;
Le passage des arguments se fait par nom. $a
, @b
et %c
sont des arguments
obligatoires, de type scalaire, tableau et hash. $d
est un argument scalaire
optionnel, car il a une valeur par défaut définie, 5. $e
est également
optionnel, mais sa valeur par défaut est dépendante de $d
. @f
et %g
sont des
arguments optionnels, avec des valeurs par défaut. La définition des arguments
est à mettre entre les balises <%args>
et </%args>
. Les variables ainsi
définies peuvent être utilisées dans le reste du composant, et seront locales
au composant. La règle a retenir est : un argument avec une valeur par défaut
est optionnel, sinon il est obligatoire.
Du côté du composant appelant, voici comment passer des arguments :
% <& composant, a => 5 &> % <& composant, b => ['une', 'référence', 'de', 'liste'] &> % <& composant, c => { un => 'hash', en => 'référence'} &>
Il est possible de faire appel à des fonctions avancées de passage d'arguments,
comme utiliser @_
ou %ARGS
directement, mais nous ne les aborderons pas
ici, car elles sont plus rarement utilisées.
Nous avons vu précédemment qu'il est possible d'accéder à un composant directement par son URL. Bien sûr, il est possible de spécifier des arguments dans l'URL. Ainsi, pour appeler le composant navigation.html en lui passant un nom de page, nous utiliserons cette syntaxe :
http://www.siteweb.com/navigation?page_courante=index
Voici comment passer en argument d'URL le tableau @b
et le hash %c
, vu
ci-dessus :
http://www.siteweb.com/composant?b=une&b=reference&b=de&b=liste http://www.siteweb.com/composant?c=un&c=hash&c=en&b=reference
Params::Validate
Ce style de passage d'argument est disponible en dehors de Mason grâce au
module Params::Validate
de Dave Rolsky (un des auteurs de Mason). Ce module
permet de passer des arguments comme le fait Mason, et de les vérifier.
Le passage d'arguments dans Mason est simple et puissant, et couvre tout type de donnée. Comme dans la quasi-totalité des frameworks web modernes, il est également transparent, c'est à dire que le développeur web n'a pas à se soucier de la provenance des arguments, ni de leur récupération manuelle. Ainsi, comme il est facile de passer des arguments d'un composant à un autre, il est possible (et recommandé) de faire beaucoup de composants distincts, un par fonctionnalité.
Il existe quelques balises spéciales en Mason, qui permettent de déclarer des sections à l'intérieur d'un composant. Les sections sont des blocs qui contiennent du Perl uniquement, qui sont donc exécutées dans un contexte particulier.
Commençons par une section simple : <%init>
. Le contenu de ce bloc sera
interprété au début du composant, même si la section <%init>
est définie à la
fin du composant. C'est utile pour séparer l'initialisation de variables Perl,
qu'on peut reléguer à la fin du composant, et le contenu HTML de la page, qu'il
est préférable de présenter au début du composant. Voici un exemple de syntaxe
de ce bloc :
%# début du composant <html> <head> <title>titre</title> </head> <body> %# Contenu de la page, qui utilise des variables Bonjour, nous sommes le <% $day %> du mois. </body> </html> %# Section init, utilisée pour initialiser $day <%init> $day = (localtime(time))[3]; </%init>
Dans cet exemple, la section <%init>
évite d'avoir à écrire la ligne
d'initialisation de $day
au tout début du composant. On peut placer <%init>
n'importe où dans le composant, on est sûr que son code sera
interprété au début du composant. Les lecteurs avertis se demanderont s'il est
possible d'avoir plusieurs sections <%init>
dans un seul composant. La
réponse est : oui, c'est possible, même si ce n'est pas conseillé. Dans le
cas de plusieurs sections <%init>
, le code sera interprété dans l'ordre
des sections <%init>
.
Il est utile de noter que <%init>
est interprété à chaque appel du composant,
contrairement à d'autres sections.
<%cleanup>
est l'inverse de <%init>
, son contenu est exécuté à la
fin du composant. <%cleanup>
est rarement utilisé, car les variables
normales du composant sont détruites à la fin de celui-ci (on dit qu'elles
sortent de la portée du composant). <%cleanup>
est donc utile uniquement
dans le cas où un traitement spécial est à effectuer à la fin, par exemple
libérer une connexion à une base de données.
<%once>
est une section dont le contenu est exécuté une seule
fois, à l'initialisation du composant. Apache crée plusieurs processus
(ou des threads, selon sa configuration), pour répondre aux requêtes des
utilisateurs du site web. Un composant est initialisé la première fois
qu'il est demandé par un utilisateur, pour un processus donné. Une fois
initialisé, le composant est précompilé, et mis en cache interne de
Mason. <%once>
permet d'exécuter du code Perl lors de cette
initialisation. <%once>
ne sera plus jamais appelé avant qu'un
nouveau processus soit créé par Apache, ou que Apache soit
redémarré.
Quelle est donc l'utilité de <%once>
? Cette section est très pratique pour
déclarer des variables, qui seront valides pendant toute la durée de vie du
processus, et qu'on pourra initialiser une seule fois. Par exemple, il est
pratique d'utiliser conjointement <%init>
et <%once>
pour créer
une connexion à une base de données permanente :
<%once> # déclaration de l'objet database handler use DBI; my $db; </%once> %# Au début de l'appel du composant, on initialise $db si besoin <%init> if (!defined $db) { $db = DBI->connect('dbi:mysql:dbname=foobar'); } </%init> %# $db est utilisable dans le composant
Dans une section <%filter>
, la variable spéciale $_
contient le
HTML final de la page, tel qu'il va être renvoyé. <%filter>
permet
donc de modifier le résultat final d'un composant : il suffit d'altérer
$_
. L'exemple suivant passe le HTML de la page en majuscule.
<%filter> y/a-z/A-Z/; </%filter>
La section <%filter>
est intéressante pour opérer une transformation sur le flux
en sortie, sans se préoccuper de la manière dont il a été créé. Cependant cela
peut nuire à la lisibilité et à la maintenabilité du code, car le fait de multiplier
les interventions sur les données, dans des endroits différents du code, peut
complexifier les processus.
Autres sections
<%shared>
Similaire à <%once>
, mais les variables déclarées dans cette section
ont leur portée limitée à la requête, et non au processus Apache.
<%def>
Permet de définir un sous-composant. La convention veut que les noms des sous-composants commencent par un point.
<%method>
Permet de définir une méthode du composant. Les méthodes seront expliquées dans le prochain article.
<%flags>
et <%attr>
Permettent d'assigner des états ou attributs aux composants. Utilisés principalement dans la programmation objet (voir le prochain article).
<%doc>
Son contenu est traité comme des commentaires.
Voici un exemple concret d'utilisation des sections. Pour un développement web conséquent, il peut être important de soigner la forme du code HTML renvoyé. Notamment, un code HTML source bien indenté est utile pour repérer et comprendre les problèmes d'affichage d'une page web. Mais indenter du HTML dans un composant Mason n'est généralement pas possible : certaines portions d'HTML sont incorporées dans certains cas uniquement, on ne peut pas prévoir l'indentation avant que les pages ne soient générées totalement.
La solution est d'indenter le HTML à la volée, avant de le renvoyer vers le navigateur de l'internaute. Ce faisant, nous pouvons également en profiter pour analyser syntaxiquement le HTML grâce à un module ad-hoc ; si cet analyseur détecte des erreurs, les lignes incriminées peuvent être affichées en bas de page, en couleur.
Après une petite recherche sur CPAN, HTML::Tidy
paraît l'outil idéal pour
vérifier la syntaxe d'un source HTML, et l'indenter. Tout d'abord, il faut
organiser le site pour que tous les composants héritent d'un composant
racine, que nous appelons tidy.html. Cela peut se faire à l'aide d'un
autohandler (voir plus bas), ou bien grâce à l'orienté objet (expliqué dans
le prochain article). Ce composant indente et analyse le contenu avant qu'il
ne soit retourné à l'internaute. Voici le code commenté. Il illustre
l'utilisation des balises <%once>
, <%init>
et <%filter>
.
%# Nous allons utiliser un objet tidy valable pour toute la durée de vie du %# processus alloué par Apache. %once est idéal pour une telle déclaration <%once> # déclaration de l'objet d'indentation du code use HTML::Tidy; my $tidy; </%once> %# Au début de l'appel du composant, il nous faut affecter $tidy, s'il n'est %# pas déjà défini. <%init> # initialisation si besoin est, pour être fork-safe if (!defined $tidy) { $tidy = HTML::Tidy->new( { config_file => '/tmp/tidy.cfg' } ); } </%init> %# On applique un filtre pour auto-indenter le html qui sort. %# Pour cela, on utilise la section %filter, qui est appelée en %# fin de calcul du composant. <%filter> # récupération du HTML my $html = $_; # on nettoie la sortie du composant $html = $tidy->clean($html); # s'il y a des erreurs ou des avertissements, on les affiche à la fin if ($tidy->messages) { # récupération des erreurs ou avertissements my @messages = $tidy->messages(); # On ajoute les erreurs et avertissements HTML à la fin du contenu # si le _type du message est à 1, c'est un warning, sinon une erreur $html .= '<br><br><hr size=2 noshade>html warnings/errors : <br>' . join("\n<br>", map { ($_->{_type} ? 'warning : ' : 'error : ') . $_->as_string; } ($tidy->messages)) . '<br><br>'; } $_ = $html; # on réinitialise l'objet tidy $tidy->clear_messages(); </%filter>
$r
et $m
: Deux couteaux suissesIl est temps de présenter rapidement les deux objets principaux accessibles
dans l'environnement Mason : $r
et $m
. Ces deux objets sont globaux, et
utilisables partout sans avoir besoin de les déclarer. En réalité, il
existe des exceptions, mais elles sont très rares. On peut considérer
qu'on a accès à $r
et $m
dans toute situation.
$r
$r
est l'objet request
de mod_perl. Il permet un accès à l'API Perl
d'Apache, ce qui est la grande force de mod_perl. Ainsi on pourra utiliser :
$r->uri; # L'URI HTTP (équivalent à l'URL dans bien des cas) $r->header_in(..); # permet de récupérer les entêtes HTTP $r->content_type; # permet de récupérer ou de changer le content-type $r->header_out(..); # permet de récupérer ou de changer un entête en sortie
$m
$m
est un accès à l'API Mason. Ainsi, $m
offre un accès au
mécanisme de recherche et d'appel aux composants, permet d'accéder aux
paramètres en cours et de les modifier. On peut même créer une
requête Mason de toutes pièces, et déclencher un appel de composant
dessus.
$m
est une instance de la classe HTML::Mason::Request
, et son API est
conséquente. Une partie est la plus souvent utilisée, il s'agit de
HTML::Mason::Component
, qui concerne les composants.
Comment travailler sur un composant ? $m
permet d'obtenir une instance de
HTML::Mason::Component
facilement :
$m->current_comp
Renvoie le composant courant.
$m->callers
Sans argument, renvoie la liste de la pile des composants parents. On peut lui passer un nombre pour récupérer un composant parent précis.
$m->fetch_comp(chemin)
Renvoie le composant correspondant au chemin.
Une fois le composant récupéré, il est possible de travailler avec. On peut récupérer son chemin, ses attributs, ses drapeaux. On peut également savoir si une méthode existe et l'appeler, etc.
Nous allons voir rapidement comment utiliser certaines méthodes de $m
dans les
chapitres sur les dhandlers et les autohandlers.
Dans le chapitre sur « Les composants Mason », nous avons expliqué rapidement comment était choisi le composant « top-level ». Le principe de base est simple : à un composant est associé un fichier du même nom. Cependant, que se passe-t-il si le fichier composant n'existe pas ? Avant de renvoyer une erreur, Mason parcours le répertoire du composant et ses répertoires parents à la recherche d'un fichier spécial appelé dhandler. Dhandler veut dire "default handler", "gestionnaire par défaut".
Dès qu'un dhandler est trouvé, Mason l'exécute et arrête sa recherche. Un
dhandler est un composant comme un autre, sauf qu'il peut utiliser à son
avantage $m->dhandler_arg
. Cette valeur de l'objet Mason ($m
), contient le
chemin vers le composant « top-level » qui n'existait pas.
Reprenons l'exemple donné dans le chapitre sur les composants, avec l'URL
http://www.siteweb.com/produits/cuillere.html?modele=7
Nous pouvons créer un dhandler pour gérer le cas d'une demande d'un produit qui
n'existe pas, et afficher un message d'erreur plus humain qu'un 404. Pour cela
nous allons créer un dhandler dans le répertoire produit, qui affichera
simplement que le produit n'existe pas. Voici le code de /produit/dhandler
:
<html> <body> <b>Il n'y a pas de <% $produit %>.</b> </body> </html> <%init> # on récupère le chemin complet (absolu) du produit demandé my $arg = $m->dhandler_arg; # on prend le dernier élément du chemin, donc le nom du composant demandé my $produit = (split('/', $arg))[-1]; # on enlève le suffixe .html, ce qui donne le nom du produit $produit =~ s/\.html$//; </%init>
Avec ce dhandler, lors de la requête, Mason renverra :
Il n'y a pas de cuillère.
Le dhandler peut donc être utilisé comme reprise sur erreur, mais nous allons voir qu'il est très intéressant de l'utiliser pour appeler le contenu adéquat. Il suffit de choisir une convention dans la syntaxe des URL, et d'utiliser le dhandler pour interpréter l'URL comme un ordre. L'exemple classique est celui du blog. Un blog est une collection de billets (petits textes) classés, qu'on peut atteindre par date ou par sujet par exemple. Les billets du blog sont stockés dans une base de données. Pour simplifier, on considérera que les billets sont stockés dans une unique table, avec une colonne pour la date et une pour le sujet du billet.
Nous pouvons convenir d'une convention d'URL comme suit :
http://www.siteweb.com/blog/par_date/20060701 http://www.siteweb.com/blog/par_sujet/informatique
Dans le répertoire /blog, nous allons écrire un dhandler qui interprétera l'URL suivant cette convention : le premier élément du chemin doit être /blog, le second indique sur quelle colonne de la table on filtre, et le troisième élément du chemin indique la valeur du filtre. Voici à quoi le dhandler ressemblera :
<html> <body> <% $contenu %> </body> </html> <%init> # on considère qu'une connexion $DBH à la base de données a été faite # on récupère le chemin my $arg = $m->dhandler_arg; # on en fait un tableau en séparant sur / my @args = split('/', $arg); # on vérifie que le premier élément du chemin est /blog shift @args eq 'blog' or $m->decline; # on prend les deux prochains éléments my ($colonne, $valeur) = @args; # on exécute la requête dans la base de donnée my $sth = $DBH->prepare( qq{SELECT contenu FROM blog WHERE ? = ? } ); $sth->execute($colonne, $valeur); my $lignes = $sth->fetchall_arrayref; # S'il n'y a pas de billet correspondant, on renvoie un 404 @$lignes or return 404; # à partir du résultat de la requête, on construit $contenu my $contenu = ... </%init>
Ce mécanisme permet d'accéder de manière souple et puissante au contenu du blog. Si un jour vous ajoutez une colonne dans la table blog, aucune modification n'est à faire sur le code Mason, pour pouvoir l'utiliser dans le site web.
Les lecteurs attentifs auront noté l'utilisation de $m->decline
. Cette
commande permet de dire que ce handler ne sait pas gérer la requête actuelle,
et que Mason doit continuer à chercher un autre handler qui saura traiter la
requête. Alors que return 404
est définitif.
Pour conclure, le dhandler est très similaire à AUTOLOAD
en Perl. Il permet
d'intercepter une requête a priori mauvaise, et d'effectuer une action à la
place.
Une fois le principe du dhandler compris, il n'est pas difficile de comprendre ce qu'est un autohandler. Son nom veut dire "automatic handler", "gestionnaire automatique", et il fonctionne par répertoire. Lorsque Mason a trouvé le composant « top-level », il vérifie juste avant de l'exécuter s'il existe un fichier appelé autohandler dans le répertoire. Si c'est le cas, il l'exécute à la place, avec une petite subtilité : le composant sera ajouté à la pile d'appel.
Le fichier autohandler est un composant classique, mis à part le moment où il est appelé. Prenons l'URL d'exemple
http://www.siteweb.com/produits/cuillere.html?modele=7
le composant top-level est cuillere.html
, dans le répertoire /produits
.
S'il existe un fichier /produits/autohandler, il va être exécuté à la place de
cuillere.html. Cependant, cuillere.html va être ajouté dans la pile d'appel de
Mason. Ainsi, si l'autohandler fait appel à $m->call_next
, alors le
composant cuillere
sera exécuté, puis Mason redonnera la main à l'autohandler.
L'intérêt immédiat est la possibilité d'encapsuler tous les composants d'un répertoire. Voici un autohandler trivial, qui ajoute un en-tête et un bas de page à tous les composants situés dans le répertoire :
<html> <head><title>Mon site web fantastique</title></head> <body> % $m->call_next; # ceci appelle le composant originel <hr /> Ceci est exemple de bas de page. </body> </html>
L'autohandler permet également de factoriser du code. Par exemple, sur un site web utilisant beaucoup de connexions à une base de données, chaque composant peut vouloir vérifier qu'il dispose d'une connexion à cette base, et la créer si besoin est. Au lieu d'écrire le code dans chaque composant, on peut le factoriser dans un autohandler.
Les autohandlers sont aussi utilisés pour inclure automatiquement des composants Mason, des modules Perl, ou même du javascript. Dans un site web utilisant beaucoup la librairie javascript Prototype, il sera intéressant d'écrire un autohandler comme suit :
<html> <head> <title>Titre du site web</title> <script src="/chemin/de/prototype-1.4.0.js"></script> </head> <body> % $m->call_next; # ceci appelle le composant originel </body> </html>
En essayant d'élargir le concept, on peut utiliser une combinaison d'un dhandler et d'un autohandler, ce qui permet de déterminer un composant top-level en fonction de règles propres au site, et d'hériter du autohandler certaines actions. On peut également avoir plusieurs autohandlers, qui seront appelés successivement. Cependant ce fonctionnement est assez difficile à gérer, et on atteint assez vite ses limitations.
Ces problèmes sont résolus par l'utilisation de la programmation orientée objet de Mason, que nous aborderons dans le prochain article.
C'est la fin de cet article, mais pas de l'aventure ! Nous venons de voir les bases de Mason, qui devraient vous permettre de vous lancer dans l'implémentation d'une première application Web dynamique. Dans le prochain article, nous aborderons entre autres les fonctionalités Orientées Objects de Mason, l'utilisation du système de cache intégré, et nous testerons un module Ajax.
MasonHQ - http://www.masonhq.com
Vue Mason pour Catalyst - http://search.cpan.org/~ank/Catalyst-View-Mason
Jifty - http://jifty.org
[1] Jifty est un framework web MVC basé sur Mason, développé principalement par Jesse Vincent. Même s'il est encore en cours de de développement, les version actuelles sont assez avancées pour être utilisées en production.
[2] L'auteur de Mason a indiqué qu'il avait baptisé le module HTML::Mason
pour suivre les conseils de nommage à l'époque, mais que s'il avait pu, il
l'aurait simplement appelé Mason
.
Copyright © Les Mongueurs de Perl, 2001-2011
pour le site.
Les auteurs conservent le copyright de leurs articles.