Retrouvez cet article dans : Linux Magazine 86
Maintenant que nous connaissons les bases de la syntaxe et de l’environnement de Smalltalk, une bonne façon d’aller plus loin et de développer notre connaissance de ce langage est d’étudier une application complète écrite en Smalltalk. Cet article présente Seaside, un framework libre pour le développement d’applications web, basé sur des composants visuels réutilisables, la définition de flot de contrôle complexe par un mécanisme transparent de continuations, la possibilité de construire des interfaces riches AJAX en utilisant du JavaScript.
Si vous souhaitez voir une application Seaside en action pour vous convaincre, regardez la vidéo de DabbleDB disponible à l’URL : http://www.dabbledb.com/utr. Dans ce premier article, nous commençons par un petit tour des fonctionnalités de base de Seaside, puis dans le prochain, nous nous intéresserons au mécanisme de type call/answer pour des flots de contrôle plus élaborés.
Une nouvelle façon de concevoir des applications web
Seaside repense radicalement la notion même d’applications web : on ne pense plus en termes de pages, de variables cachées et autres concepts liés au protocole HTTP.
Avec Seaside, on développe des applications web comme des applications de bureau, c’est-à-dire en définissant des objets ou composants visuels, puis en les assemblant et ceci sans se préoccuper des pages suivantes et précédentes.
Le flot de contrôle d’un composant visuel peut être élaboré librement et utilise les boucles et conditions qui sont disponibles au niveau du langage Smalltalk.
Contrairement au modèle de servlets qui nécessite un handler pour chaque page ou requête, Seaside représente l’entière session comme un morceau de code avec un flot de contrôle linéaire et naturel : les pages s’appellent les unes les autres comme des invocations de méthodes, les formulaires sont de simples objets, les objets sont passés comme des références, plus besoin de les tronçonner en URL ou champs cachés et tout ceci avec la possibilité de gestion du bouton de retour en arrière et de la concurrence inhérente aux navigateurs web.
Seaside offre aussi un modèle d’évènements basé sur des call-backs, une approche simple de la génération d’XHTML, un ensemble de composants réutilisables, une gestion des templates pour les concepts graphiques, et un ensemble d’outils de développement.
Premier contact
Le plus simple pour démarrer avec Seaside est de récupérer une image Squeak toute faite disponible sur le site http://www.seaside.st/ (version Seaside 2.6b1 avec Scriptaculous 70).
Il est également possible d’installer un par un les différents composants nécessaires à l’utilisation de Seaside.
Seaside utilise un serveur web écrit en Smalltalk nommé « Comanche » qui tourne par défaut sur le port 8080 dans cette image.
Vous pouvez exécuter l’expression : WAKom stop pour arrêter le serveur web Comanche puis : WAKom startOn: 9090 pour commencer sur un autre port.
Pour vérifier que Seaside marche, une fois l’image Squeak démarrée : ouvrir un navigateur web (Firefox de préférence) à l’URL : http://localhost:8080/seaside/counter
Cela doit faire apparaître la plus petite application fournie avec le cadre d’application Seaside : un simple compteur avec deux liens pour incrémenter et décrémenter (voir figure 1).

Fig. 1 – L’application de base fournie avec Seaside : le compteur
Commençons par jouer avec ce compteur et vérifions qu’il fonctionne correctement : cliquez sur le lien ++ pour augmenter la valeur du compteur et sur le lien -- pour diminuer la valeur. Pendant que vous jouez avec le compteur, regarder l’URL dans la barre d’adresse de votre navigateur.
L’URL possède deux paramètres, qui peuvent chacun nous donner des indications sur comment Seaside fonctionne. D’abord, il y a une longue, et apparemment aléatoire série, de lettres et de chiffres qui ressemble à _s=ZXsNyDAuodddtmEG et qui ne change jamais au cours de l’utilisation de l’application.
C’est la clé unique qui identifie la session utilisateur courante. La session est un concept central pour les applications Seaside, comme on verra plus loin.
Contrairement à la plupart des cadres d’applications web, qui fournissent un objet session où il est possible de stocker des données et qui se basent majoritairement sur des cycles requêtes/réponses individuelles, Seaside utilise la session comme un thread ou un processus : il lance une application à un point précis et l’application continue à partir de ce point, faisant quelques pauses pour afficher les pages web et interagir avec l’utilisateur. Nous reparlerons de ceci en détail lorsque nous parlerons de flot de contrôle.
Une des forces de Seaside est que chaque élément graphique (composant visuel) peut avoir un flot de contrôle spécifié indépendamment du reste de l’application et composé pour une application donnée : ceci comme pour le développement d’applications desktop.
Le second paramètre (identifié par _k) est une plus courte suite de lettres et de nombres. Ce paramètre ne se réfère pas à une action particulière, ni ne code un état de la session ; tous les états sont stockés du coté du serveur, et une session Seaside progresse linéairement, sans sauter d’une action à une autre.
En fait, ce paramètre garde simplement la trace des demandes de la session courante et permet à Seaside de savoir où se trouve l’utilisateur dans l’application. De la même façon, chaque lien ou élément de formulaire d’une page possède un identifiant unique et séquentiel : le sens est uniquement attaché à ces nombres quand ils reviennent au serveur.
Cela a le désavantage de rendre les marque-pages incompréhensibles ou même inutilisables, mais apporte une énorme flexibilité. Tout est géré comme nous allons le voir de manière transparente par Seaside.
Seaside gère également les problèmes liés à l’utilisation du « back-button » : essayez, par exemple, d’incrémenter votre compteur jusqu’à 6, puis pressez deux fois sur le bouton de votre navigateur web permettant de revenir en arrière. Pressez de nouveau sur le ++, eh oui, vous obtenez 5 et non 7. Nous vous dirons comment cela est réalisé dans le prochain article.
Les composants visuels : génération de code XHTML
Nous allons commencer par quelques applications simples : un « hello world » amélioré, un additionneur et enfin un jeu, ces exemples vont nous permettre d’introduire les mécanismes clés de Seaside.
Notre but est de pouvoir vous montrer comment ces applications peuvent être composées très simplement par la suite pour former une application plus complexe.
Un composant Seaside a trois responsabilités distinctes : le maintien de l’état d’un objet d’une application, l’affichage de l’interface utilisateur en XHTML et enfin l’interaction avec l’utilisateur.
Pour définir un nouveau type de composant visuel, il suffit de construire une classe qui dérive de la classe WAComponent (WA comme Web Application). Construisons la classe HelloWorldComponent comme suit :
WAComponent subclass: #HelloWorldComponent instanceVariableNames: ‘’ classVariableNames:’’ poolDictionaries: ‘’ category: ‘LinuxMagazine86’
Smalltalk est généralement considéré comme le langage ayant inventé le concept de programmation orienté objet. Il a, par exemple, largement influencé des langages comme Obj-C, sorte de croisement entre Smalltalk et C.
Lorsque Seaside a besoin d’afficher un composant, il envoie au composant le message renderContentOn: en passant comme argument une instance de la classe WAHtmlRenderer.
Ceci est une façon de procéder habituelle pour tous ceux qui ont déjà implémenté une applet Java ou créé une sous-classe de Morph en Squeak : on associe un canevas (zone graphique) à l’objet, qui va l’utiliser pour s’afficher.
Dans notre cas, plutôt que dessiner des formes et des lignes, le « canevas » sait comment afficher des éléments XHTML. L’afficheur marche comme un flot : chaque message qui lui est envoyé permet d’ajouter du texte ou des éléments au document.
Voici par exemple la méthode renderContentOn: de notre classe : HelloWorldComponent.
renderContentOn: html html heading:’Hello World’
Il reste maintenant à dire à Seaside comment associer une adresse web (une URL) à ce composant :
HelloWorldComponent registerAsApplication: ‘hello’
En ouvrant un navigateur à l’adresse : http://localhost:8080/seaside/hello, il est possible de voir notre superbe application. Comment cela marche ? Lorsque le navigateur a fait une requête à cette adresse, le serveur Seaside a créé une instance de la classe HelloWorldComponent et a généré le code XHTML en utilisant la méthode renderContentOn:.
La méthode heading: génère une balise de type H (H1 par défaut) autour du texte passé en paramètre.
Avec Seaside, on n’écrit pas directement du code XHTML. On préfère faire la présentation au moyen d’un programme Smalltalk. Les méthodes utilisables sur les instances de la classe WAHtmlRenderer sont calquées sur les balises XHTML.
L’avantage d’utiliser cette approche est qu’un code Smalltalk valide va donner une page XHTML valide. Pas besoin alors de s’embêter avec la fermeture des balises par exemple.
En ce qui concerne les questions de présentation, il y a deux façons d’associer une feuille de style CSS à un composant, soit de manière programmatique, soit en utilisant le halo. Dans le premier cas, on construit pour chaque sous-classe de WAComponent, une méthode style qui retourne une chaîne de caractères. Par exemple :
style
^’h1 {text-align: center; color: red}’
associe à un composant, une feuille de style où le contenu des balises <h1> est centré et affiché en rouge.
Cette information de style peut être également fournie sous une forme visuelle (par exemple par le graphiste du site web qui ne connaît pas Smalltalk) en utilisant le mécanisme de halos de l’interface Seaside, comme nous le verrons dans le prochain article.
Comme cette introduction à Seaside est orientée développeur, nous vous invitons à consulter une documentation CSS pour construire des mises en pages plus sophistiquées.
Voilà un rendu XHTML plus complexe, avec un en-tête de niveau 1 (une balise <H1>) et un paragraphe (balise <p>):
renderContentOn: html html heading: ‘Hello world’ level: 1. html paragraph: ‘Bienvenue, lecteurs de Linux Magazine. Vous trouverez ici bientôt tout ce qu’il faut pour faire une application Seaside.’
Comment ce principe de génération de balises XHTML par programme peut marcher avec des balises enchâssées dans d’autres balises comme par exemple pour des listes ? Ceci est élégamment géré en utilisant les blocs Smalltalk.
Pour rappel, un bloc Smalltalk est l’équivalent d’une fermeture lexicale qui permet de geler un ensemble d’expressions pour être évalué un peu plus tard.
Reprenons de nouveau notre méthode renderContentOn:.
renderContentOn: html
html heading: ‘Hello world’ level: 1.
html paragraph: ‘Bienvenue sur mon site Seaside. Ceci est une liste de couleurs :’.
html orderedList:
[html listItem: ‘Rouge’.
html listItem: ‘Bleu’.
html listItem: ‘Vert’.
html listItem: ‘Orange’]
On voit ici que la méthode orderedList: génère la balise de début de liste ordonnée et prend en paramètre un bloc d’éléments qui vont être rendus successivement pour être enchâssés dans cette balise. L’enchâssement naturel du code Smalltalk va générer l’enchâssement des balises XHTML. La même approche peut être utilisée pour générer des tables, ici avec deux niveaux d’imbrications :
renderContentOn: html
html heading: ‘Hello world’ level: 2.
html paragraph: ‘Bienvenue sur ce site Seaside. Voici une table XHTML :’.
html table: [html tableRow: [html tableData: ‘X’.
html tableData: ‘Y’.
html tableData: ‘Z’].
html tableRow: [html tableData: ‘1’.
html tableData: ‘2’.
html tableData: ‘3’].
html tableRow: [html tableData: ‘A’.
html tableData: ‘B’.
html tableData: ‘C’]]
Nous vous invitons à consulter l’API de la classe WAHtmlRenderer pour savoir tout ce qu’il est possible de générer par programme.
Des ancres et des formulaires
Passons maintenant à la possibilité de définir des ancres entre différentes parties de notre application. Les ancres ou liens hypertextuels permettent de circuler dans une application web et de déclencher des actions (ajouter un article dans un panier par exemple).
En Seaside, les ancres sont spécifiées par des blocs d’action un peu comme le seraient des callbacks d’une interface utilisateur. Lorsque l’utilisateur suit le lien, l’action correspondante est effectuée.
Il n’y a pas besoin d’associer une page différente à l’ancre, cela s’effectue de manière transparente par le framework.
On redéfinit la méthode renderContentOn: de HelloWorldComponent comme suit :
renderContentOn: html html anchorWithAction: [self sayHello] text: ‘Hello Linux Magazine’
et on rajoute une méthode sayHello dans la classe HelloWorldComponent :
sayHello self inform: ‘Hello!’
Exécutons de nouveau le composant dans notre navigateur web. Une ancre « Hello Linux Magazine » a été créée. Lorsque l’on clique dessus, le contenu du bloc est exécuté (i. e. la méthode sayHello).
La méthode inform: affiche un message d’information suivi d’un bouton [ok]. Si on presse le bouton [ok], on revient à la vue originale du composant. Finalement, pour ajouter un peu plus d’interactivité, nous souhaiterions connaître le nom de notre interlocuteur. Pour cela, nous ajoutons une variable d’instance username et modifions encore une fois la méthode renderContentOn:
WAComponent subclass: #HelloWorldComponent
instanceVariableNames: ‘username’
classVariableNames: ‘’
poolDictionaries: ‘’
category: ‘LinuxMagazine86’
renderContentOn: html
html heading:’Hello World’ level:1.
html form:
[ html text: ‘Nom utilisateur : ‘.
html textInputWithCallback: [ :value | username := value ].
html break.
html submitButtonWithAction: [ self inform: ‘Salut ‘ , username ]
text: ‘Dis Salut’ ]
La méthode form: permet de créer un formulaire, comprenant une zone d’entrée de texte (textInputWithCallback:) et un bouton : submitButtonWithAction:. Lorsque l’utilisateur a renseigné la zone d’entrée de texte et appuyé sur le bouton, le premier bloc permet de mettre la chaîne de caractères rentrée par l’utilisateur dans la variable d’instance username, puis le second bloc est exécuté. Il affiche « Salut » suivi du nom de l’utilisateur.
De la même façon, on peut générer d’autres éléments habituels de formulaires comme des listes déroulantes ou des radios-boutons.
Notez que nous pourrions mettre n’importe quelle expression Smalltalk dans l’action du « submit button ». On voit que le flot de contrôle de l’application web s’exprime très naturellement avec Seaside.
À la différence d’autres frameworks similaires, il n’y a pas besoin de gérer des pages et des ancres entre les pages. Cela revient quasiment à écrire une application Smalltalk classique. Ce point en détail sera vu dans le prochain article.
Les tâches : gestion du workflow
On a souvent besoin de composants plus simples que les composants dérivants de la classe WAComponent. Un WATask est un composant Seaside qui ne fait pas de rendu directement et qui est souvent utilisé pour représenter une séquence d’actions que l’utilisateur doit nécessairement effectuer, incluant plusieurs composants visuels.
Par exemple sur un site de commerce électronique, l’utilisateur doit d’abord s’authentifier, puis mettre un article dans un panier, puis payer au moyen de sa carte de crédit.
Les composants WATask dérivent de la classe WAComponent, mais il n’est plus nécessaire de redéfinir la méthode renderContentOn:, ils disposent d’une seule méthode go.
Construisons par exemple un additionneur. Pour cela, nous définissons une classe Adder comme sous-classe de WATask avec une méthode go :
WATask subclass: #Adder instanceVariableNames: ‘’ classVariableNames: ‘’ poolDictionaries: ‘’ category: ‘LinuxMagazine86’ go | value1 value2 | value1 := self request: ‘Nombre 1 :’. value2 := self request: ‘Nombre 2 :’. self inform: value1 asNumber + value2 asNumber
Après avoir enregistré ce composant, nous pouvons le tester maintenant avec un navigateur web sur l’URL : http://localhost:8080/seaside/adder.
Le jeu du nombre à deviner
Programmons un petit jeu en utilisant WAComponent : l’utilisateur doit deviner une valeur aléatoirement choisie par la machine comprise entre 1 et 100.
Commençons par construire une sous-classe de WAComponent représentant les différentes opérations que doit faire l’utilisateur pour deviner un nombre : SecretNumber.
On définit la méthode renderContentOn: suivante :
renderContentOn:html html heading:’Jeu du nombre a deviner’ level:1. html form:[html submitButtonWithAction:[self play] text:’Lancer une nouvelle partie’
ainsi que la méthode play qui gère la logique du jeu :
play
|response number attemptNumber|
attemptNumber := 0.
number := 100 atRandom.
self inform:’Je choisis un nombre entre 1 et 100. Essayez de le deviner.’.
[attemptNumber := attemptNumber +1.
response := (self request:’Essai numero ‘, attemptNumber asString) asNumber.
(response > number) ifTrue:[self inform:’Nombre trop grand.’].
(response < number) ifTrue:[self inform:’Nombre trop petit.’].
response == number] whileFalse.
self inform:’Bravo vous avez decouvert le nombre secret en ‘,
attemptNumber asString, ‘ essais.’
Cette méthode est très simple : on tire un nombre au hasard, puis on demande à l’utilisateur un nombre jusqu’à ce qu’il trouve la bonne valeur.
On compte également le nombre d’essais nécessaire à l’utilisateur pour trouver le nombre secret.
Il est de nouveau nécessaire d’enregistrer le composant SecretNumber :
SecretNumber registerAsApplication:’secret’.
Mais il est également possible d’utiliser la page de configuration de Seaside. Ajoutez au préalable la méthode de classe :
SecretNumber class>>canBeRoot ^true
Indiquant que ce composant peut se trouver à la racine d’une application. Consultez la page http://localhost:8080/seaside/config (Seaside vous demande un login et un mot de passe, ce sont respectivement « admin » et « seaside » par défaut).
Ajouter une nouvelle application en entrant le chemin d’accès à votre composant (secret) et en cliquant sur add. Choisir ensuite dans l’interface de configuration du chemin comme Root Component, la classe SecretNumber.
Vous pouvez maintenant tester cette application.

Fig. 2 – Les trois premières étapes du jeu
Composons toutes ces applications
Maintenant que nous avons défini ces petites applications, nous souhaitons obtenir une application qui les regroupe toutes.
Cet exemple illustre un des aspects fondamentaux de Seaside : comment plusieurs composants développés séparément peuvent être composés pour en former un plus complexe.
L’idée est de pouvoir former des bibliothèques de composants visuels.
WAComponent subclass: #AllInAll
instanceVariableNames: ‘children’
classVariableNames: ‘’
poolDictionaries: ‘’
category: ‘LinuxMagazine86’
children
^children
children: aCollection
children := aCollection
initialize
self children: { Adder new. Adder new. HelloWorldComponent new.
SecretNumber new. WACounter new. }
renderContentOn: html
self children do: [:each | html render: each ]
separatedBy: [ html horizontalRule ]
Dans cette classe AllInAll, la variable d’instance children est une collection qui contient les sous-composants, qui sont créés dans la méthode initialize. La méthode renderContentOn: effectue un rendu de chacun des composants séparés par une barre horizontale.

Fig. 3 – 5 applications vivant leurs vies séparément
Et voilà, une fois que vous avez enregistré l’application AllInAll, vous obtenez une application ayant cinq sous-applications qui fonctionnent indépendamment. Chacune disposant de son propre flot de contrôle et gérant son propre état. Cet exemple, bien que simple, illustre la possibilité de créer des librairies de widgets qui sont réutilisables entre projets. Bien évidemment, le flot de contrôle de l’application mère peut être bien plus complexe comme nous le montrerons lors de notre prochain article. Maintenant que vous connaissez un peu plus Seaside, vous pouvez analyser la classe de compteur WACounter que nous avons utilisé au début de cette présentation.
En conclusion
Seaside est un framework web original qui commence à faire parler de lui. Seaside repense complètement le développement d’applications web en se libérant des contraintes liées au protocole HTTP. Comme nous l’avons vu, il n’est plus nécessaire de concevoir son application sous formes de pages liées par des ancres.
On programme une application web comme s’il s’agissait d’une application normale. De nombreuses applications industrielles commencent à être développées en Seaside, comme pour la Sacem Suisse, des gestions d’assurance maladie et autres boutiques en ligne.
Pour vous mettre en appétit pour le prochain article, nous vous conseillons de regarder comment l’application de vente de Sushi, fournie avec Seaside, est réalisée (voir ici : http://localhost:8080/seaside/store/) ainsi que de regarder comment Seaside gère AJAX en jouant avec le site : http://scriptaculous.seasidehosting.st/seaside/scriptaculous/.
Généralement, une des difficultés de diffusion d’un nouveau framework est liée à l’absence d’hébergement. Depuis peu de temps, il est possible d’héberger des applications Seaside sur : http://seasidehosting.st./
Même si ce site ne propose pas d’hébergement de bases de données, il vous permettra de mettre vos premières œuvres en ligne. Seaside est diffusé sous une licence MIT. Vous pouvez télécharger les exemples de cet article en ligne ici : http://www.info.unicaen.fr/~serge/share/lm86/
Liens :
- Le site officiel : http://www.squeak.org/
- Le site de Seaside : http://www.seaside.st/
- Le Wiki de la communauté française :
http://community.ofset.org/wiki/Squeak - La partie du Wiki de la communauté française sur Seaside : http://community.ofset.org/wiki/Seaside
- Des livres gratuits en ligne sur Smalltalk et Squeak : http://www.iam.unibe.ch/~ducasse/FreeBooks.html
- Un livre sur Squeak en français : BRIFFAULT (X.) et DUCASSE (S.), Squeak, Eyrolles, 2002 ;
http://www.iam.unibe.ch/~ducasse/Books.html :
Retrouvez cet article dans : Linux Magazine 86

