Table des matières
JSON est format de représentation des données, ses principales qualités sont :
Il s'agit d'un format texte, donc facilement lisible.
Sa légèreté et la facilté pour l'expoiter
Défini par la RFC 4627, il est ouvert
De plus comme son nom l'indique ( JavaScript Object Notation), il est le format d'échange de Javascript. Il est donc parfaitement adapté lors de l'échange de données en Ajax.
En Javascript on déclare un objet comme ceci:
var personne = {
prenom: 'Gaston',
nom: 'Lagaffe'
};En JSON l'équivalent sera:
{"nom":"Gaston","prenom":"Lagaffe"}
Un tableau en Javascript sera représenté comme cela:
var personnes = [
{
prenom: 'Gaston',
nom: 'Lagaffe'
},
{
prenom: 'Joe',
nom: 'Dalton'
},
{
prenom: 'Jean',
nom: 'Dupont'
}
];En JSON ce sera :
[{"prenom":"Gaston","nom":"Lagaffe"},{"prenom":"Joe","nom":"Dalton"},{"prenom":"Jean","nom":"Dupont"}]Simple non ?
Le parrallèle avec Perl est flagrant :
#!/usr/bin/perl
use JSON;
my $personne = {
nom => "Gaston",
prenom => "Lagaffe",
};
my $personnes = [
{
prenom => 'Gaston',
nom => 'Lagaffe'
},
{
prenom => 'Joe',
nom => 'Dalton'
},
{
prenom => 'Jean',
nom => 'Dupont'
}
];
print to_json($personne) . "\n";
print to_json($personnes) . "\n";Après cette petite mise en bouche examinon Jemplate.
Jemplate signifie " JavaScript Templating with Template Toolkit", il s'agit d'un framework écrit en Perl permettant de compiler un template ( Template toolkit ) en Javascript.
Parfaitement adapté pour faire de l'Ajax, on pourra l'utiliser conjointement avec, par exemple, un 'Onclick' pour exécuter :
Jemplate.process('my-template.tt', 'url/data.json', '#some-div');
Ainsi le template my-template.tt sera une première fois compiler, éventuellement mis en cache, interprétera les données 'url/data.json' dans le template et remplacera le '<div id="some-div"> par le résultat du template.
Catalyst dispose d'une Vue pour traiter ce type de résultat : Catalyst::View::Jemplate
Jemplate nécessite qu'on lui fournisse du JSON, cela peut être fait simplement avec une Vue prévue à cet effet : Catalyst::View::JSON
Nota: Nous aurrions aussi pu retourner du JSON avec une action REST mais cela est une autre histoire, j'y reviendrai surement dans un autre article.
Après avoir installé le module Catalyst::View::JSON, constuisons notre application ainsi qu'un controleur Test
perl -MCPAN -e "install Catalyst::View::JSON" cd /tmp catalyst.pl MyApp cd MyApp ./script/myapp_create.pl Controller Test
Première étape créer une Vue JSON :
./script/myapp_create.pl view JSON JSON
Pour nous éviter quelques surprises nous indiquerons quelle sera la
variable 'stash' a utiliser pour exposer les données à la vue JSON. Pour
cela ajoutons au fichier principal de l'applcation
lib/MyApp.pm les lignes suivantes (avant le
setup de l'application):
...
__PACKAGE__->config({
'View::JSON' => {
expose_stash => [ qw(data) ],
},
});
...
Notre url JSON (l'action du controleur Test ) se nommera data_json, nous l'accédereons par http://localhost:3000/test/data_json
Ajoutons cette action au controleur
lib/MyApp/Controller/Test.pm :
sub data_json : Local {
my ( $self, $c ) = @_;
my @data = ();
push(@data, {
prenom => "Gaston",
nom => "Lagaffe",
});
push(@data, {
prenom => "Joe",
nom => "Dalton",
});
$c->stash->{ 'data' } = \@data;
$c->detach( $c->view( 'JSON' ) );
}Nous avons stockées les données @data dans le 'stash', celles ci sont ensuite transmisent à la Vue JSON.
En se connectant à http://localhost:3000/test/data_json, le fichier data_json apparait.
cat data_json
{"data":[{"prenom":"Gaston","nom":"Lagaffe"},{"prenom":"Joe","nom":"Dalton"}]}OK c'est bien cohérent :)
Notre controleur Test comporte une action
index, celle utilisée par défaut en accédant à
http://localhost:3000/test . Nous lui ajouterons un
template index.tt qui va nous permettre d'effectuer
de l'Ajax. Modifions cette action dans le fichier
lib/MyApp/Controller/Test.pm pour qu'elle devienne
:
sub index : Private {
my ( $self, $c ) = @_;
$c->stash->{template} = "index.tt";
}Puisque nous utilisons un template TT il est nécessaire de lui fournir une Vue :
./script/myapp_create.pl view TT TTSite
Et
pour que cette dernière soit utilisée par défaut pour tous nos templates
nous ajouterons la ligne suivante au fichier de configuration de notre
application myapp.yml :
default_view: TT
Plaçons maintenant
notre template i ndex.tt dans
' root/src' l'emplacement par défaut des templates (
paramétré par la variable INCLUDE_PATH de la vue
TT)
Template root/src/index.tt<br /> <a href="/test" onclick="my_function_jemplate(); return false">Test</a> <div id="myjson">myjson</div>
Lorsque nous cliquerons sur le lien 'Test' la fonction Javascript my_function_jemplate sera appelée.
Jemplate appelé par la fonction 'my_function_jemplate' va interprété un template 'my_jemplate.tt' avec les données JSON.
Pour indiquer à Jemplate où chercher ses templates nous ajouterons
au fichier principal de l'applcation lib/MyApp.pm ces
quelques lignes:
__PACKAGE__->config->{cache}{expires} = 43200;
__PACKAGE__->config->{cache}{backends}{jemplate}{store} = 'FastMmap';
__PACKAGE__->config->{'View::Jemplate'}{jemplate_dir} = __PACKAGE__->path_to('root', 'jemplate');
__PACKAGE__->config->{'View::Jemplate'}{jemplate_ext} = '.tt';Les templates de Jemplate seront donc placés dans le répertoire
root/jemplate et porterons l'extension 'tt'. Lorsque
ces derniers seront compilés lors de leur première utilisation il seront
mis en cache.
Fabriquons le template
root/jemplate/my_jemplate.tt :
mkdir root/jemplate
template root/jemplate/my_jemplate.tt<br /> [% FOREACH d IN data %] prenom=[% d.prenom %]<br /> nom=[% d.nom %]<br/> [% END %]
Il ne nous reste plus qu'a ajouter le framework Jempate pour créer la glue de tout celà.
Il nous faudra une Vue Jemplate, le runtime Jemplate et une action 'jemplate' :
./script/myapp_create.pl view Jemplate Jemplate jemplate --runtime > root/static/Jemplate.js
et l'action
'jemplate' a créer dans le controleur
lib/MyApp/Controller/Root.pm :
sub jemplate : Global {
my($self, $c) = @_;
$c->forward('View::Jemplate');
}Entre les balises 'head' du fichier
root/lib/site/html nous ajouterons la prise en charge du
Javascript Jemplate.js, de tous les fichiers template du répertoire
root/jemplate et notre fonction Javascript my_function_jemplate:
...
<script type="text/javascript"
src="[% base %]static/Jemplate.js"></script>
<script type="text/javascript"
src="[% base %]jemplate"></script>
<script type="text/javascript">
function my_function_jemplate () {
Jemplate.process('my_jemplate.tt',
'[% Catalyst.uri_for("/test/data_json") %]',
'#myjson');
}
</script>
...
Ouf c'est terminé et ça fonctionne, si on clique sur le lien 'Test' l'application nous retourne le JSON interprété par le template :)
Template root/src/index.tt Test template root/jemplate/my_jemplate.tt prenom=Gaston nom=Lagaffe prenom=Joe nom=Dalton
La mise en place peu sembler un peu laborieuse mais lorsque tout ceci est en place l'Ajax devient trivial, pour une autre fonction Ajax il suffit d'ajouter un controleur retournant les données en JSON, un template pour les interpréter et la fonction Javascript pour appeler Jemplate.
Lors de la configuration de Jemplate nous lui avons spécifié de
mettre en cache les templates compilés. Pour le cache fonctionne il est
nécessaire à l'application de charger le plugin Cache. Cela se fait en
modifiant comme suit le fichier lib/MyApp.pm :
use Catalyst qw/-Debug ConfigLoader Static::Simple/;
devient
use Catalyst qw/-Debug
ConfigLoader
Static::Simple
Cache Cache::Store::FastMmap
/;
Ne pas oublier que les templates Jemplate
sont compilés, le résultat de cette compilation est placé dans le
répertoire /tmp/myapp. Lors de test il est prudent à
chaque fois de supprimer ce répertoire. Il faudra aussi penser à la
variable expose_stash lors de l'export de données en
JSON, on peut cependant toujours utiliser la même variable, cela ne me
semble pas absurde.
Une dernière remarque, le contexte de Catalyst n'est pas prise en charge par Jemplate, ainsi nous ne pourrons par exemple accéder à c.config.name :(
Pour y remédier il est nécessaire de transformer les variables du contexte en données JSON avant la compilation. $c->stash->{data} .= { config => $c->config->{name}, ...};
Je n'ai pas réussi à intégrer FormFu à Jemplate :( Si quelqu'un a une idéee elle serait la bienvenue.
Les sources et le binaire de l'application sont disponibles ICI.
Utilisation à partir des sources:
cd /tmp wget http://dab.free.fr/files/articles/perl/catalyst/MyApp_Jemplate.tgz perl Makefile.PL make make test ./script/myapp_server.pl
Le binaire est dans les sources, il contient toutes les librairies nécessaires.
./myapp_jemplate myapp_server.pl


