Balade avec Seaside

From OFSET Wiki

Jump to: navigation, search

Seaside


Traduction de : Tutorial: A Walk on the Seaside


Ce tutoriel est un guide introductif au framework web Seaside 2.7.

Pour commencer, vous aurez besoin d'une installation fonctionnelle de Squeak en version 3.9 avec Seaside :

La connaissance du Smalltalk et de notions de HTML sera utile aussi. La maîtrise de l'environnement Squeak pourra vous aider.

Un nom d'utilisateur et un mot de passe par défaut ont été configurés - ils sont utilisés par l'utilitaire de configuration Seaside que nous verrons plus tard. Il s'agit de :

  • username: admin
  • password: seaside

Quand l'image est démarrée, vous devez lancer le service Seaside. Seaside fournit une classe WAKom pour pouvoir utiliser le serveur web Commanche. Pour lancer commanche configuré pour Seaside, évaluez la ligne suivant dans un Workspace :

WAKom startOn: 9090.

Seaside doit maintenant écouter sur le port 9090. À chaque fois que vous rouvrirez l'image à partir de maintenant, le service Seaside sera automatiquement lancé.

Contents

[edit] Concepts de base: Sessions et Composants

Pour vérifier que Seaside fonctionne, ouvrez un navigateur à l'adresse : http://localhost:9090/seaside/counter. Cela doit faire apparaître la plus petite application Seaside : un simple compteur avec deux liens pour incrémenter et décrémenter. Commençons par jouer un peu avec et vérifions qu'il fonctionne correctement : cliquez sur le lien "++" pour augmenter le nombre et sur le lien "--" pour le diminuer.

Pendant que vous jouez avec le compteur, faite attention à 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, 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. Contrairement à la plupart des frameworks web, qui fournissent un objet session comme un endroit où garder des données, et qui se base majoritairement sur des cycles requêtes/réponses individuelles, Seaside utilise la session comme un thread ou un processus : il lance une application a un point précis et l'application continue linéairement à partir d'ici, faisant quelques pauses pour afficher les pages web et attendre l'utilisateur. Nous regarderons ceci plus en détail lorsque nous parlerons de flux de contrôle, un peu plus loin dans ce tutoriel.

Vous noterez un lien "New Session" (nouvelle session) sur une barre grise en bas de la page. C'est la barre d'outils de Seaside, qui apparaît pendant le développement ; nous explorerons quelques options au fur et à mesure. Le lien "New Session" arrête le processus actuel et en lance un nouveau qui commence au début. Si vous appuyez dessus maintenant, la seule chose que vous noterez sera le compteur qui reviendra à zéro et le numéro de session qui changera.

Le second paramètre (identifié par _k) est une plus courte et aléatoire 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 sur la page possède un identifiant unique et séquentiel : le sens est uniquement attaché à ces nombres quand ils reviennent au serveur. Ça a le désavantage de rendre les marques-pages incompréhensibles ou même inutilisables, mais cela apporte une énorme flexibilité.

Au lieu de pages nommées ou d'actions, les applications Seaside utilisent habituellement des composants qui interagissent, des objets qui modélisent l'état et la logique d'une partie de l'interface graphique. Quand la session commence, une unique instance de composant est créée, et ce composant entre dans une boucle de réponse : il s'affiche à l'utilisateur et attend une action. L'action (un lien cliqué ou un formulaire soumis) va lancer la méthode correspondante sur le composant, et quand cette méthode sera terminée, le composant s'affichera à nouveau. Ces méthodes peuvent simplement mettre à jour l'état de l'application ou du composant en cours, ou elle peuvent créer et transférer le contrôle à un autre composant, qui entrera à son tour dans une boucle de réponse.

La classe de composant qui modèle l'application "counter" s'appelle WACounter, et nous allons la regarder plus en détail maintenant.

[edit] État, Action, Affichage : l'essentiel des composants

Chaque composant a trois responsabilités : maintenir l'état de l'interface utilisateur, réagir aux interactions de l'utilisateur et s'afficher en HTML. Regardons rapidement comment WACounter prend en compte chacune d'entre elles.

[edit] État

Même si l'état d'une application est conservé dans des objets métiers ou une base de données, l'interface utilisateur a généralement son état propre. Ceci peut inclure, par exemple, l'état courant d'un champ ou quel enregistrement d'une base de données est en cours d'affichage ou encore quel noeud d'un arbre a été déplié. Tout cela est conservé dans les variables d'instances des composants qui forment l'interface.

Dans le cas du WACounter, le seul état dont il faut se soucier c'est le compteur lui-même. Lorsque l'instance de WACounter est créé au début de la session, elle initialise sa variable d'instance count à 0. En cliquant sur les liens "++" ou "--", on change simplement la valeur de la variable d'instance. Une bonne façon de tester cela est de cliquer sur "Toggle Halos" dans la barre d'outils, ce qui rajoute un certain nombre de nouvelles icones. Cliquer sur l'icone en forme d'oeil qui apparait en haut de la page. Ceci fera apparaître un inspecteur web sur le composant WACounter. Vous verrez un WACounter avec ses variables d'instances hérités de sa superclasse WAComponent avec également à la fin la variable d'instance count. La valeur courante de count sera toujours la même que celle de page initiale où vous avez lancé l'inspecteur.

Si vous voulez un peu explorer, le lien sur une variable d'instance lance un nouvel inspecteur pour cet objet; vous pouvez utiliser le chemin généré pour revenir en arrière. Lorsque vous avez fini, fermez juste la fenêtre de l'inspecteur. Pour enlever les icones, appuyer de nouveau sur "Toggle Halos".

[edit] Action

Il y a deux actions possibles pour l'application compteur et WACounter a une méthode correspondante pour chacunes d'entre elles. En cliquant sur le lien "++", on appelle la méthode #increase, qui est une méthode très simple :

increase
   count := count + 1

Ceci fait ce que l'on attend : l'incrémentation de la valeur de count de 1. Lorsque la méthode se finit, la boucle de réponse continue et le composant s'affiche lui-même avec la nouvelle valeur.

Pour le plaisir, essayons de modifier cela. À côté de l'icone du halo en forme d'oeil, il y a une icone en forme de clé à molette qui permet d'avoir un navigateur de classes fonctionel dans votre navigateur web (grace à Lukas Renggli) qui s'ouvre sur la classe du composant courant. Trouver la méthode #increase et changer la de telle façon à incrémenter le compteur de deux au lieu de un. N'oubliez pas de cliquer sur Accept. Puis fermer le navigateur et appuyer de nouveau sur le lien "++".

[edit] Affichage

Lorsque Seaside a besoin d'afficher un composant, il envoie au composant le message #renderContenOn: en passant une instance de WAHtmlRenderer comme argument. Ceci est une façon de procéder habituelle pour tout ceux qui ont déjà implémenter une applet Java ou créer une sous-classe de Morph en Squeak : on associe un canvas (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 HTML. 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: du WACounter :

renderContentOn: html
   html heading: count.
   html anchor 
      callback: [self increase]; 
      with: '++'.
   html space.
   html anchor
      callback: [self decrease]; 
      with: '--'
  

Trois messages différents sont envoyés à l'afficheur, chacun d'entre eux produit un élément HTML différent. Le premier #heading produit un entête de section : si la valeur courante de count est 42, cela ajoutera : <h1>42</h1> au document. #space est également très simple, puisque cela ajoute un espace non-sécable. #anchor et #callback: sont plus intéressants. Ces méthodes sont appellées de manière évidente pour produire les liens "++" et "--" qui apparaissent sous le compteur. Et le message with: spécifie clairement la chaîne de caractères qui apparait dans le lien. Mais vers quoi pointent ces liens ? Où vont-ils ?

Une réponse courte est : ne vous en souciez pas. En Seaside, les liens n'ont pas de destinations, ils ont des callbacks : chaque fois que vous générez un lien ou un bouton, il est associé avec un bloc. Lorsque ce lien ou ce bouton particulier est utilisé, le bloc est évalué. Dans ce cas, en cliquant sur le lien "++", on lance : self increase.

Essayons de modifier un peu la méthode : au lien de liens pour incrémenter et décrémenter, nous allons utiliser des boutons. Trouver la méthode WACounter>>renderContentOn: dans le navigateur de classe Squeak et changer le code de la façon suivante :

renderContentOn: html
   html form:  [
      html heading: count.
      html submitButton
         callback: [self increase];
         text: '++'.
      html space.
      html submitButton
         callback: [self decrease];
         text: '--'
    ].
    

La chose principale que nous avons réalisé est de changer les appels à #anchor #callback: et #with: en #submitButton #callback: #text: Les méthodes sont utilisées quasiment de la même façon : c'est vraiment facile de passer des liens aux boutons suivant ce que votre interface nécessite. Néanmoins les boutons marcheront uniquement s'ils sont à l'intérieur d'une forme. La méthode #form: est très simple, elle prend en paramètre un simple bloc. Cette fois ci, ce n'est pas un callback; c'est plutôt utilisé pour structurer afin d'enfermer le contenu d'une forme. En utilisant #form: ceci permet d'assurer que tout se trouve bien dans une forme. La même convention est utilisée pour les méthodes pour générer des tables : #table:, #tableRow: #tableData:.

[edit] La boucle de réponse

Maintenant que nous avons pris connaissance des responsabilités d'un composant, il peut être utile de voir comment elles sont reliées les unes aux autres. Chaque composant effectue une une boucle de réponse, s'affichant lui-même, puis attendant une entrée de l'utilisateur, traitant l'entrée et s'affichant de nouveau. En utilisant les méthodes que nous avons déjà vues, voici à quoi cela ressemble :

  • Une instance de WACounter est créé; count est initialisé à 0.
  • Seaside appelle #renderContentOn: sur le compteur. Ceci produit un document HTML affichant la valeur courante de count, ainsi que les liens associés avec les callbacks. Ce document HTML est envoyé à l'utilisateur.
  • L'utilisateur clique sur l'un des liens, invoquant le callback correspondant, qui a son tour appelle soit #increase, soit #decrease. Ceci modifie la valeur de count. La méthode d'action est alors rappelée.
  • Le compteur est de nouveau affiché avec son nouvel état.

[edit] Allons un peu plus loin : interactions entre les composants

Afin de transférer le contrôle vers un autre composant, WAComponent procure la méthode spéciale #call:. Cette méthode prend un composant comme paramètre et va démarrer immédiatement la boucle de réponse de ce composant en l'affichant à l'utilisateur. Pour tester ceci, vous pouvez utiliser la classe de composant WAFormDialog. On peut modifier la méthode #decrease afin d'afficher un message si l'utilisateur essaie d'aller en dessous de 0 :

decrease
  count = 0
     ifFalse: [count := count - 1]
     ifTrue: [self call: (WAFormDialog new addMessage: 'Lets stay away from negatives.'; yourself)]


Si count est égal à 0, ceci créé une nouvel instance de WAFormDialog, qui affiche un message à l'écran et l'appelle. Maintenant démarrons une nouvelle session et essayons d'appuyer sur le lien "--". Le compteur devrait disparaître et devriez voir le dialogue à la place avec le message affiché comme un grand entête.

Afficher un dialogue comme cela est si commum que WAcomponent procure un méthode #inform: pour le faire, imitant en cela la méthode #inform: de Object. Essayer de changer #decrease afin de l'utiliser :

decrease
  count = 0
     ifFalse: [count := count - 1]
     ifTrue: [self inform: 'Lets stay away from negatives.']


Vous pouvez remarquer qu'en plus du message, le dialogue est accompagné d'un bouton avec le label "Ok". Que se passe-t-il si on appuie dessus ? Et bien, le dialogue disparait et le compteur est de nouveau affiché. Si on regarde ce qui se passe, l'instance de WAFormDialog appelle une méthode compagnon de #call: appellé #answer, qui permet de retourner le contrôle au composant qui a appellé. En effet, appellé un autre composant correspond juste à appel de sous-routine : si vous préférez, on peut voir le #call: comme mettant sur une pile le nouveau composant et #answer comment dépilant cette pile pour revenir à l'ancien.

En fait, il n'y a pas de pile qui est maintenu. Ce que fait #answer est un peu plus compliqué : il provoque le retour de l'appel original de la méthode #call: et le programme continue a partir de ce point. Comme l'appel au dialogue est la dernière expression exécuté dans #decrease, tout ce qui se passe est que decrease retourne et la boucle de réponse du compteur continue.

Ceci est complètement contre-intuitif et nécessite un peu plus d'explications. Découpons la séquence de ce qui se passe exactement :

  1. WACounter>>decrease fait un appel à WAComponent>>inform:
  2. WAComponent>>inform: créée une nouvelle instance de WADialog et la passe en paramètre à WAComponent>>call: Appellons ce point dans la séquence, le "point d'appel".
  3. WAFormDialog commence sa boucle de réponse et s'affiche lui-même dans le navigateur de l'utilisateur.
  4. L'utilisateur clique sur le bouton "Ok". Ceci a comme conséquence que WAFormDialog invoque WAComponent>>answer.
  5. On arrive à la partie étrange. WAComponent>>answer ne revient jamais. C'est parce qu'il fait un saut: le contrôle saute au "point d'appel", juste après l'appel à #call.
  6. WAComponent>>call: retourne à WAComponent>>inform:.
  7. WAComponent>>inform: retourne à WACounter>>decrease.
  8. WACounter>>decrease retourne à la boucle de réponse du compteur et s'affiche de nouveau.

La chose "cool" a propos de #call: est celle la : si un composant appellé procure un argument à #answer:, cet argument sera retourné à partir du #call:. En d'autre termes, appeller un composant peut provoquer un résultat. Ceci est bien plus puissant que simplement empiler et dépiler des composants sur une pile. Par exemple, il est alors facile d'implémenter #confirm: qui affiche une question et retourne "true" ou "false" suivant ce que l'utilisateur clique, afin d'aller avec #inform:. Essayons de modifier #decrease comme ce qui suit :

decrease
  count = 0
    ifFalse: [count := count - 1]
    ifTrue:
        [(self confirm: 'Do you want to go negative?')
               ifTrue: [self inform: 'Ok, lets go negative!'.
               count := -100]].


Si vous jouez avec le compteur maintenant, vous allez réaliser que dans cette simple méthode, il y a trois pages différentes : la confirmation du dialogue, le message du dialogue pour : "Ok, let's go negative" et le retour vers l'affichage du compteur lui-même (essayer d'appuyer sur le bouton back pendant la séquence et regarder ce qui se passe).

[edit] Une structure typique

Ceci est une structure typique pour une application Seaside : plutôt que d'avoir une série de pages fortement couplés, où chaque page sait quelles pages viennent avant ou viennent après, ici chaque page rassemble et retourne une seule unité d'information à l'utilisation, avec une logique qui permet de faire marcher le tout. Le résultat est que l'on a du code très réutilisable.

Faire des appels et des retours n'est pas la seule façon d'utiliser les composants Seaside. Vous ne l'avez peut-être pas réalisé, mais il y a u moins toujours deux composants Seaside sur votre écran à chaque instant. L'un est le WACounter, quel est l'autre ? La réponse est ce que vous voyez à chaque instant est une instance de WACounter enchassé dans un autre composant, une instance de WAToolFrame qui affiche la barre d'outils. Cette forme d'enchassement est très commune en Seaside et les pages sont formées de plusieurs composants individuels emboités les uns dans les autres. Les détails concernant l'emboîtement de ces composants dépasse ce simple tutoriel, mais vous pouvez aller consulter un exemple simple en regardant WAMultiCounter(http://localhost:9090/seaside/multi), qui contient plusieurs WACounter indépendant. N'oublier pas de cliquer sur "Toggle Halos" pour explorer cet exemple.

[edit] Aller de l'avant : démarrer vos propres applications

Avec un peu de chance, maintenant vous avez plus de connaissances sur à quoi ressemble une application Seaside. Pour commencer à jouer avec les votres, vous pouvez jetter un coup d'oeil à l'application de configuration ici : http://localhost:9090/seaside/config Elle permet facilement d'ajouter une nouvelle application nommée et d'associer avec elle la classe composant de votre choix. Vous aurez besoin du nom et du mot de passe que vous avez choisi lors de l'installation pour y avoir accès. Si vous écrivez un nouveau composant et vous voulez qu'il apparaisse comme option dans l'application de configuration, il faut impléter la méthode #canBeRoot comme méthode de classe qui retourne true.

Personal tools