Catalyst, JSON et Jemplate

user_icon admin | icon2 Catalyst | icon4 20/4/2008 17h13| Type doc: article| Type File: txt| icon3 No Comment

Catalyst, JSON et Jemplate


1. JSON

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.

2. 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

3. L'url nous retourne du JSON

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 :)

4. Squelette de l'application

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.

5. Ajout de la couche Jemplate

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.

6. Source et binaire

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


Add_a_comment

Validator_logo
Catapulse v0.06
( 0.080544 s)