Showing posts with label seaside. Show all posts
Showing posts with label seaside. Show all posts

Saturday, January 30, 2010

Seaside-Hosting compatible Pharo

L'hébergeur gratuit (pour des applications non-commerciales) Seaside-Hosting accepte maintenant les images Pharo ! J'en ai donc profité pour déployer ma première image Seaside... et c'est d'une simplicité étonnante.

Il suffit juste de créer un compte, uploader son image, clicker sur Start et c'est fini. Cool.

J'ai donc mis en ligne une image avec SimpleWebDoc: http://magaloma.seasidehosting.st/

Sunday, December 6, 2009

Pharo et documentation des packages

Voici quelques expérimentations faites alors que je voulais explorer les documentations contenues dans Pharo. Dans ce billet nous verrons comment ressortir les commentaires des classes d'un package donné. Puis je décrirai le codage d'une application Seaside minimaliste pour visualiser cette documentation depuis un navigateur Web.

1. Voyage au centre du package

Les classes sont regroupées dans des packages gérées par Monticello. On peut faire l'analogie avec les gems du monde Ruby ou les gestionnaires de paquets des distributions Linux.

Chaque package étant une instance de la classe MCPackage, on peut en sortir la liste. Ouvrez un Workspace et affichez le résultat du code suivant (via click-droit, print it)

MCPackage allInstances



Cela devrait vous retourner une longue liste de packages:



Comptons en le nombre en évaluant la taille du tableau retourné:

MCPackage allInstances size.

Sur mon image je trouve 155 packages.

Chaque package a un nom via l'accesseur name. On peut ainsi en sortir la liste:

MCPackage allInstances collect: [:p| p name]

En triant par ordre alphabétique:

(MCPackage allInstances collect: [:p| p name]) sort

Affichons le tout dans un Transcript. Ouvrez-le via le menu World > Tools > Transcript.



Puis évaluez le code suivant dans le Workspace:

|packageNames|
packageNames := MCPackage allInstances collect: [:p| p name].
packageNames sort do:[:name| Transcript show:name; cr]





Les objects PackageInfo référencent toutes les classes d'un package. Chaque objet MCPackage est associé à un objet PackageInfo.

Prenons par exemple le premier package et affichons toutes les classes qu'il contient via l'accesseur classes:

MCPackage allInstances first packageInfo classes




Pour extraire toutes les classes d'un package donné, une petite sélection fait l'affaire. Le code suivant renvoie toutes les classes du package System-Tools:

|systemTools|
systemTools := (MCPackage allInstances select: [:p| p name = 'System-Tools']) first.
systemTools packageInfo classes.





On peut accéder au commentaire de chaque classe via l'accesseur comment. Affichons le commentaire de la classe SpaceTally dans un Transcript:

Transcript show:SpaceTally comment



De là à afficher toutes les classes d'un package avec leurs commentaires, il n'y a qu'un pas:

|systemTools|
systemTools := (MCPackage allInstances select: [:p| p name = 'System-Tools']) first.
systemTools packageInfo classes do:[:c|
    Transcript show: '***',c name; cr.
    Transcript show: c comment; cr; cr]






2. Application Seaside pour consulter les commentaires

Rajoutons maintenant un peu de bling-bling à tout ça. Un Transcript c'est bien, une page web c'est plus rigolo.

Créons le package SimpleWebDoc qui contiendra une application Seaside minimaliste permettant de parcourir les commentaires des classes des packages.

Quelques précisions pour commencer. J'utilise ici l'image Pharo 1.0 RC1 09.11.04 avec Seaside et Pier, téléchargeable sur le site de Pharo à l'heure où j'écris ces lignes.
Le navigateur de classes utilisé par défaut est Browser (pour des raisons de performances et stabilité), or je vais utiliser le Package Browser. Pour changer le navigateur par défaut, ouvrez le navigateur de classe et dans le menu de la fenêtre sélectionnez Choose new default Browser.



Ici sélectionnez O2PackageBrowserAdaptator.



Lancez un nouveau navigateur de classe, vous devriez obtenir le Package Browser.



Ajoutons maintenant le package SimpleWebDoc: click droit, create package.



Nouveau clic droit sur le package et choisissez various > add to smart groups pour le rajouter dans l'onglet groups du Package Browser.





Créer application Seaside nécessite d'hériter WAComponent et de l'enregistrer comme application. Voici la déclaration de la classe SWDocView:

WAComponent subclass: #SWDocView
 instanceVariableNames: ''
 classVariableNames: ''
 poolDictionaries: ''
 category: 'SimpleWebDoc'

Pour afficher un composant, Seaside lui envoie le messager renderContentOn: en lui passant un objet WARenderCanvas sur lequel dessiner notre page web (en utilisant des brosses/brush). Nous allons ici construire un formulaire affichant tous les packages:

renderContentOn: html
 html form: [
  html select 
   list: (MCPackage allInstances collect: [:p| p name])
 ].

Améliorons la lisibilité du code en créant allPackageNames qui retourne les noms de tous les packages, dans l'ordre alphabétique:

allPackageNames 
 ^ (MCPackage allInstances collect: [:p| p name]) sort

et ajustons renderContentOn: en conséquence:

renderContentOn: html
 html form: [
  html select 
   list: self allPackageNames;
 ].

Testons ce que donne ce premier code. Déclarez le composant SWDocView comme application en évaluant ce qui suit dans un Workspace:

SWDocView registerAsApplication: 'simple-web-doc'

Pointez ensuite votre navigateur Web à l'adresse: http://localhost:8080/seaside/simple-web-doc



Ajoutons un bouton pour valider le formulaire:

renderContentOn: html
 html form: [
  html select 
   list: self allPackageNames;
  html submitButton with:'show'
 ].

Maintenant la partie intéressante: lorsqu'on valide le formulaire, on affiche la documentation du package. Pour cela nous stockons le nom sélectionné dans la variable d'instance selectedPackage en utilisant le callback de la brosse WASelectTag:

renderContentOn: html
 html form: [
  html select 
   list: self allPackageNames;
   callback: [:value| selectedPackage := value].
  html submitButton with:'show'
 ].

Si selectedPackage est définit, affichons la documentation:

renderContentOn: html
 html form: [
  html select 
   list: self allPackageNames;
   callback: [:value| selectedPackage := value].
  html submitButton with:'show'
 ].
 
 selectedPackage ifNotNil: [
   self renderPackage:selectedPackage On:html.
 ]

La méthode SWDocView>>renderPackage:On: qui reprends l'exemple précédent:

renderPackage:packageName On:html
  |package|
  package := (MCPackage allInstances select: [:p| p name = packageName]) first.
  package packageInfo classes do: [:c| 
    html heading level:2; with: c name.
      html break.
      html text: c comment.
      html horizontalRule.
  ]

et voici le résultat:



Amenons un peu de lisibilté en écrivant une méthode qui retourne toutes les classes d'un package:

classesOfPackageNamed: packageName
 |package|
 package := (MCPackage allInstances select: [:p| p name = packageName]) first.
 ^ package packageInfo classes

d'où une mise à jour de renderPackage:On:

renderPackage:packageName On:html
 (self classesOfPackageNamed:packageName) do: [:c| 
  html heading level:2; with: c name.
  html break.
  html text: c comment.
  html horizontalRule.
  ]


Bon nombre de choses sont à réaliser pour avoir une navigation correcte. Pour aller plus loin, consultez Dynamic Web Development with Seaside.

Tuesday, June 16, 2009

Jouons avec Pharo et Seaside

J'ai assisté il y a quelque temps a une conférence de Stéphane Ducasse à l'Institut de Management d'Annecy. Co-créateur de projet Pharo et développeur actif de la communauté Smalltalk, Stéphane nous a présenté différents outils dont Seaside, un framework dédié à l'écriture d'applications Web (en insistant sur le terme application).

Pharo est une implémentation Open Source du langage Smalltalk, qui se veut moderne, agile et cohérente. Pharo est né comme un fork de Squeak, puis a évolué en nettoyant l'implémentation des parties obsolètes, bancales ou instables, en simplifiant l'intéraction avec l'environnement de développement et en intégrant des outils de développement efficaces. De plus, chose essentielle, ils ont créé un joli logo.




La première version stable 1.0 ne devrait plus trop se faire attendre (vers l'automne ?)

J'avais déjà assisté a une présentation de Seaside aux RMLL à Mont-de-Marsan. Mais je voulais d'abord découvrir Smalltalk et du coup je suis passé à côté d'un des principaux fonctionnements novateurs qu'apporte Seaside au développements Web.

Stéphane a parcouru un diaporama de Lukas Renggli "The Heretic Web Framework" disponible ici.

Prenons un exemple simple d'application "classique": nous voulons demander deux nombres et afficher le résultat dans une console. En python nous pourrions coder comme suit:

value1 = input("Premier nombre: ")
value2 = input("Second nombre: ")
print(value1+value2)

Tant que l'utilisateur n'a pas saisi de nombre, le programme reste en attente sur la fonction input Nous utilisons le retour de la fonction pour stocker les deux valeurs.

Imaginons la même application en environnement Web. Le programme de calcul fonctionne côté serveur et l'utilisateur se sert de son navigateur Web pour saisir les valeurs. La plupart des systèmes actuels nous propose de créer une vue ou template pour définir l'interface utilisateur, un contrôlleur pour exécuter les requêtes client et renvoyer le résultat. Nous devons généralement implémenter un graphe d'état et manipuler les sessions pour savoir si nous en sommes à la demande de la première ou seconde valeur.

Seaside permet de programmer de la même manière qu'une application classique. L'équivalent Seaside/Smalltalk du code python précèdent est:

| value1 value2 |
value1 := self request: ‘Premier nombre’.
value2 := self request: ‘Second nombre’.
self inform: value1 asNumber + value2 asNumber.

On n'est pas dépaysé....


Maintenant un peu de concret. Téléchargez la machine virtuelle Pharo et l'image intégrant Seaside sur le site du projet.



Sous Linux, on doit malheureusement bricoler un peu. Décompressez la machine virtuelle puis activez le bit d'exécution du binaire squeak:
unzip pharo-vm-0.15.1b-linux.zip
chmod +x pharo-vm-0.15.1b-linux/squeak
On peut ensuite décompresser l'image et la lancer:
unzip pharo0.1-10324web09.06.1.zip
pharo-vm-0.15.1b-linux/squeak pharo0.1-10324web09.06.1/pharo0.1-10324web09.06.1.image

L'image ouverte, Seaside tourne sur le port 8080. Vous pouvez le vérifier en pointant votre navigateur Web à l'adresse http://localhost:8080/seaside.



Dans Seaside, les points d'entrée de nos applications doivent être définis. Nous allons créer une nouvelle classe de type WATask, un "contrôlleur" Seaside, et l'assigner à une URL.

Les classes peuvent être définies un utilisant le Class Browser, via le menu World ouvert par un clic sur le bureau Pharo. Dans la première colonne, sélectionnez Unpackaged (tout en bas) pour créer une nouvelle classe sans la placer dans un package particulier. Dans la zone de saisie du bas, saisissez le code suivant pour déclarer la classe Additionneur qui hérite WATask.
WATask subclass: #Additionneur
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''
    category: 'Unpackaged'

Ouvrez le menu contextuel de la zone de saisie via un clic droit et sélectionnez accept pour enregistrer la classe.




Ceci fait, notre classe doit se déclarer comme application accessible directement via un point d'entrée. Pour trouver ce type d'application, Seaside parcours toutes les classes héritant WAComponent (qu'hérite WATask et donc Additionneur) et sélectionne celles dont le retour de la méthode canBeRoot est true.

(Mise à jour: d'autres moyens sont disponibles, voir le commentaire de Damien)

Pour ajouter une méthode de classe, sélectionnez la classe Additionneur dans la seconde colonne puis cliquez sur le bouton class pour visualiser les méthodes de classe. Sélectionnez la catégorie -- all -- dans la troisième colonne, puis entrez le code suivant dans la zone de saisie:
canBeRoot 
    ^true
Enregistrez alors la méthode via la commande accept du menu contextuel.

Vérifions que notre Additionneur peut être accessible depuis l'interface de configuration de Seaside. Ouvrez la page en question depuis votre navigateur Web à l'adresse suivante: http://localhost:8080/seaside/config. Le compte par défaut est admin / seaside.

Dans la section Settings, Add an entry point, saisissez le nom de votre nouveau point d'entrée, par exemple additionneur. Je précise que ce nom est indépendant du nom de la classe (au cas où vous seriez un développeur Rails ;) ). Sélectionnez Application comme Type puis cliquez sur Add.



Vous entrez alors dans la page de configuration de l'application. Sous la section Configuration/General, modifiez le paramètre Root Component pour sélectionnez la classe Additionneur dans la liste déroulante. Cliquez enfin sur Save en bas de la page.

En cliquant sur Close vous revenez à la page principale de configuration de Seaside.

Vous devriez voir apparaître le point d'entrée additionneur associé à la classe Additionneur dans la liste /seaside/.


Si vous cliquez sur le lien maintenant, vous aurez une erreur car nous n'avons pas encore implémenté le fonctionnement de notre application, ce que nous allons écrire de suite.


Retournez dans Pharo. Seaside cherche à évaluer la méthode go d'une instance de notre classe Additionneur. Pour l'ajouter, sélectionnez d'abord les méthodes d'instance en cliquant sur le bouton instance en dessous de la seconde colonne du Class Browser. Cliquez ensuite sur la catégorie -- all -- et saisissez le code présenté précédemment:
| value1 value2 |
value1 := self request: ‘Premier nombre’.
value2 := self request: ‘Second nombre’.
self inform: value1 asNumber + value2 asNumber.
puis enregistrez-la (clic droit, accept).




Maintenant pointez votre navigateur sur l'application à l'adresse suivante: http://localhost:8080/seaside/additionneur et goûtez à la joie d'avoir une application qui fonctionne du premier coup.




Si ce n'est pas le cas, je décline toute responsabilité quant aux dommages irréversibles que pourraient causer mes indications sur la santé de votre ordinateur...