% -*- mode: latex; -*- mustache tags:  
\ifx\wholebook\relax\else

	\documentclass[10pt,twoside,english]{root/support/latex/sbabook/sbabook}
  \usepackage{import}
  \subimport{root/support/latex/}{common.tex}

  \hypersetup{pdfinfo = {
    Title = {{Pharo Mooc}},
    Author = {},
    Keywords = {pharo, smalltalk}}}
  \begin{document}
\fi

\chapter{TinyChat}
Pharo permet la d\'{e}finition en quelques dizaines de lignes un server REST gr\^{a}ce au package Teapot qui \'{e}tend Zinc, le superbe client/server HTTP de Pharo 
d\'{e}velopp\'{e} par la soci\'{e}t\'{e} BetaNine et offert gracieusement \`{a} la communaut\'{e}. 
L'objectif de ce chapitre est de vous faire d\'{e}velopper en cinq classes, une application de chat client/server avec un client graphique. Cette petite aventure
vous permettra de vous familiariser avec Pharo et de voir l'aisance avec laquelle un server REST peut \^{e}tre d\'{e}fini. 
D\'{e}velopp\'{e}e en quelques heures, TinyChat a \'{e}t\'{e} con\c{c}u comme une application p\'{e}dagogique. Dans cet objectif, \`{a} la fin de l'article nous proposons 
une liste d'am\'{e}liorations possibles. 
\section{Objectifs et architecture}
Nous allons donc construire un serveur de discussion (chat) et un client permettant de s'y connecter (voir figure \ref{tinychatclient}). 


\begin{figure}

\begin{center}
\includegraphics[width=0.8\textwidth]{figures/tinychatclient.png}\caption{Chatting avec TinyChat\label{tinychatclient}}\end{center}
\end{figure}


La communication entre le client et le serveur sera bas\'{e}e sur HTTP et REST.
En plus des classes \textcode{TCServer} et \textcode{TinyChat} (le client), nous d\'{e}finirons trois autres classes : la classe \textcode{TCMessage} qui repr\'{e}sente les messages \'{e}chang\'{e}s (dans le future vous pourrez \'{e}tendre TinyChat pour \'{e}changer des \'{e}l\'{e}ments plus structur\'{e}s comme JSON ou STON le format textuel Pharo.), la classe \textcode{TCMessageQueue} qui stocke les messages et \textcode{TCConsole} l'interface graphique.
\section{Chargeons Teapot}
Vous pouvez charger Teapot en utilisant le Configuration Browser qui se trouve dans le menu Tools du menu principal. S\'{e}lectionnez Teapot et utilisez \symbol{34}Install Stable\symbol{34}.
Une autre solution consiste \`{a} utiliser le script suivant :

\begin{displaycode}{smalltalk}
Gofer it
    smalltalkhubUser: 'zeroflag' project: 'Teapot';
    configuration;
    loadStable.
\end{displaycode}
\section{Repr\'{e}sentation d'un message}
Un message est un objet tr\`{e}s simple avec un texte et un identifiant pour l'\'{e}metteur. 
\subsection{Classe TCMessage}
Nous d\'{e}finissons la classe \textcode{TCMessage} dans le package \textcode{TinyChat}.

\begin{displaycode}{smalltalk}
Object subclass: #TCMessage
	instanceVariableNames: 'sender text separator'
	classVariableNames: ''
	category: 'TinyChat'
\end{displaycode}

Les variables d'instances sont les suivantes:

\begin{itemize}
\item \textcode{sender} : le login de l'exp\'{e}diteur,
\item \textcode{text} : le texte du message,
\item \textcode{separator} : un caract\`{e}re s\'{e}parateur pour l'affichage.
\end{itemize}
\subsection{Cr\'{e}ez les accesseurs pour les variables d'instance 'sender' et 'text'}
Nous cr\'{e}ons les accesseurs suivants: 

\begin{displaycode}{smalltalk}
TCMessage >> sender
	^ sender

TCMessage >> sender: anObject
	sender := anObject

TCMessage >> text
	^ text

TCMessage >> text: anObject
	text := anObject
\end{displaycode}
\subsection{Initialisation de la classe}
La m\'{e}thode \textcode{initialize} d\'{e}finit la valeur du caract\`{e}re s\'{e}parateur.

\begin{displaycode}{smalltalk}
TCMessage >> initialize
	super initialize.
	separator := '>'.
\end{displaycode}

La m\'{e}thode de classe \textcode{TCMessage class\textgreater{}\textgreater{}from:text:} permet d'instancier un message :

\begin{displaycode}{smalltalk}
TCMessage class >> from: aSender text: aText
	^ self new sender: aSender; text: aText; yourself
\end{displaycode}

Le message \textcode{yourself} rend le receveur du message : c'est une mani\`{e}re de s'assurer que le nouvel objet cr\'{e}\'{e} sera
bien retourn\'{e} par le message \textcode{from:text:} et non le r\'{e}sultat du message \textcode{text:}. 
\subsection{Convertir un message en cha\^{i}ne de caract\`{e}res}
Nous ajoutons une m\'{e}thode \textcode{printOn:} pour transformer le message en une cha\^{i}ne de caract\`{e}res.
Le mod\`{e}le de la cha\^{i}ne est sender-separator-text-crlf. Exemple: 'john\textgreater{}hello !!!'.
La m\'{e}thode \textcode{printOn:} est invoqu\'{e}e par la m\'{e}thode \textcode{printString}. Il est important de comprendre que la m\'{e}thode \textcode{printOn:} 
est invoqu\'{e}e par les outils tels que le d\'{e}bogueur ou l'inspecteur d'objets.

\begin{displaycode}{smalltalk}
TCMessage >> printOn: aStream

	aStream 
		<< self sender; << separator; 
		<< self text; << String crlf
\end{displaycode}
\subsection{Construire un message \`{a} partir d'une cha\^{i}ne de caract\`{e}res}
Nous devons \'{e}galement d\'{e}finir deux m\'{e}thodes pour cr\'{e}er un message \`{a} partir d'une chaine, ayant la forme: \textcode{'olivier\textgreater{}tinychat est cool'}.
Tout d'abord, cr\'{e}ons une m\'{e}thode de classe qui sera invoqu\'{e}e de la mani\`{e}re suivante: \textcode{TCMessage fromString: 'olivier\textgreater{}tinychat est cool'},
puis la m\'{e}thode d'instance remplissant les variables de l'objet pr\'{e}alablement cr\'{e}\'{e}. 

\begin{displaycode}{smalltalk}
TCMessage class >> fromString: aString
	^ self new 
		fromString: aString;
		yourself
\end{displaycode}

\begin{displaycode}{smalltalk}
TCMessage >> fromString: aString
	"Compose a message from a string of this form 'sender>message'."
	| items |
	items := aString subStrings: separator.
	self sender: items first.
	self text: items second.
\end{displaycode}

Maintenant nous sommes pr\^{e}ts pour d\'{e}finir le serveur. 
\section{Le serveur}
Pour le serveur, nous allons d\'{e}finir une classe pour g\'{e}rer une queue de messages. Ce n'est pas vraiment n\'{e}cessaire mais cela permet de bien 
identifier les responsabilit\'{e}s.
\subsection{Stockage des messages}
Cr\'{e}ez la classe \textcode{TCMessageQueue} dans le package TinyChat-Server. 

\begin{displaycode}{smalltalk}
Object subclass: #TCMessageQueue
	instanceVariableNames: 'messages'
	classVariableNames: ''
	category: 'TinyChat-server'
\end{displaycode}

La variable d'instance \textcode{messages} est une collection ordonn\'{e}e donc le contenu est compos\'{e} d'instances de \textcode{TCMessage}.
Une OrderedCollection est une collection qui s'agrandit dynamiquement lors d'ajouts.

\begin{displaycode}{smalltalk}
TCMessageQueue >> initialize
	super initialize.
	messages := OrderedCollection new.
\end{displaycode}
\subsubsection{Op\'{e}rations de bases sur la liste des messages}
On doit pouvoir ajouter un message \textcode{add:}, effacer la liste avec \textcode{reset} et conna\^{i}tre le nombre de messages avec \textcode{size}.

\begin{displaycode}{smalltalk}
TCMessageQueue >> add: aMessage
	messages add: aMessage 

TCMessageQueue >> reset
	messages removeAll

TCMessageQueue >> size
	^ messages size
\end{displaycode}
\subsubsection{Obtenir la liste des messages \`{a} partir d'une position}
Lorsqu'un client demande au serveur la liste des derniers messages \'{e}chang\'{e}s, il indique au serveur l'index du dernier message qu'il conna\^{i}t. 
Le serveur r\'{e}pond alors la liste des messages re\c{c}us depuis cet index.

\begin{displaycode}{smalltalk}
TCMessageQueue >> listFrom: aIndex
	^ (aIndex > 0 and: [ aIndex <= messages size]) 
		ifTrue: [ messages copyFrom: aIndex to: messages size ]
		ifFalse: [ #() ]
\end{displaycode}
\subsubsection{Formatage des messages}
La classe \textcode{TCMessageQueue} doit pouvoir formater une liste de messages (\`{a} partir d'un index) en une cha\^{i}ne de caract\`{e}res que le serveur pourra transmettre au client. 
On ajoute ensuite une m\'{e}thode \`{a} la classe \textcode{TCMessageQueue} pour construire une seule chaine de caract\`{e}res \`{a} partir de chaque cha\^{i}ne de caract\`{e}res produite par chaque message :

\begin{displaycode}{smalltalk}
TCMessageQueue >> formattedMessagesFrom: aMessageNumber
	
	^ String streamContents: [ :formattedMessagesStream |  
		(self listFrom: aMessageNumber) 
			do: [ :m | formattedMessagesStream << m printString ] 
		]
\end{displaycode}
\subsection{Le Serveur de Chat }
Le coeur du serveur est bas\'{e} sur le framework REST Teapot, permettant l'envoi et la r\'{e}ception des messages. 
Il maintient en plus une liste de messages qu'il communique aux clients.
\subsection{Cr\'{e}ez la classe TCServer dans le package TinyChat-Server}
\begin{displaycode}{smalltalk}
Object subclass: #TCServer
	instanceVariableNames: 'teapotServer messagesQueue'
	classVariableNames: ''
	category: 'TinyChat-Server'
\end{displaycode}

La variable d'instance \textcode{messagesQueue} r\'{e}f\'{e}rence la liste des messages re\c{c}us et envoy\'{e}s par le serveur. 

\begin{displaycode}{smalltalk}
TCServer >> initialize
	super initialize.
	messagesQueue := TCMessageQueue new.
\end{displaycode}

La variable d'instance \textcode{teapotServer} r\'{e}f\'{e}rence l'instance du serveur TeaPot que l'on cr\'{e}\'{e}e \`{a} l'aide de la m\'{e}thode \textcode{initializePort:}

\begin{displaycode}{smalltalk}
TCServer >> initializePort: anInteger
	teapotServer := Teapot configure: { 
		#defaultOutput -> #text.
		#port -> anInteger.
		#debugMode -> true
	}.
	teapotServer start.
\end{displaycode}

Le routage HTTP est d\'{e}fini dans la m\'{e}thode \textcode{registerRoutes}. Trois op\'{e}rations sont d\'{e}finies :

\begin{itemize}
\item GET \textcode{messages/count} : retourne au client le nombre de messages re\c{c}us par le serveur,
\item GET \textcode{messages/\textless{}id:IsInteger\textgreater{}} : le serveur retourne les messages \`{a} partir de l'index indiqu\'{e} dans la requ\^{e}te HTTP,
\item POST \textcode{/message/add} : le client envoie un message au serveur. 
\end{itemize}

\begin{displaycode}{smalltalk}
TCServer >> registerRoutes
	teapotServer
		GET: '/messages/count' -> (Send message: #messageCount to: self);
		GET: '/messages/<id:IsInteger>' -> (Send message: #messagesFrom: to: self);
		POST: '/messages/add' -> (Send message: #addMessage: to: self)
\end{displaycode}

Nous exprimons ici que le chemin message/count va donner lieu \`{a} l'execution du message \textcode{messageCount} sur le serveur lui-m\^{e}me.
Le pattern \textcode{\textless{}id:IsInteger\textgreater{}} indique que l'argument doit \^{e}tre exprim\'{e} sous forme de nombre et qu'il sera converti en un entier.

La gestion des erreurs est construite dans la m\'{e}thode \textcode{registerErrorHandlers}. Ici on voit comment on construit une instance de la classe \textcode{TeaResponse}.

\begin{displaycode}{smalltalk}
TCServer >> registerErrorHandlers
	teapotServer
		exception: KeyNotFound -> (TeaResponse notFound body: 'No such message')
\end{displaycode}

Le d\'{e}marrage du serveur est confi\'{e} \`{a} la m\'{e}thode \textcode{TCServer class\textgreater{}\textgreater{}startOn:} qui re\c{c}oit le num\'{e}ro de port TCP en param\`{e}tre.

\begin{displaycode}{smalltalk}
TCServer class >> startOn: aPortNumber
	^self new
		initializePort: aPortNumber;
		registerRoutes;
		registerErrorHandlers;
		yourself
\end{displaycode}

Il faut \'{e}galement g\'{e}rer l'arr\^{e}t du serveur. La m\'{e}thode \textcode{stop} met fin \`{a} l'ex\'{e}cution du serveur TeaPot et vide la liste des message.

\begin{displaycode}{smalltalk}
TCServer >> stop
	teapotServer stop.
	messagesQueue reset.
\end{displaycode}

Comme il est probable que vous executiez plusieurs fois l'expression \textcode{TCServer startOn:}, nous d\'{e}finissons la m\'{e}thode de classe \textcode{stopAll} qui stoppe tous les serveurs
en r\'{e}cuperant toutes les instance de la classe.  La m\'{e}thode \textcode{TCServer class\textgreater{}\textgreater{}stopAll} demande l'arr\^{e}t de chaque instance du serveur.

\begin{displaycode}{smalltalk}
TCServer class >> stopAll
	self allInstancesDo: #stop
\end{displaycode}
\subsection{Traitements r\'{e}alis\'{e}s par le serveur}
La m\'{e}thode \textcode{addMessage} extrait de la requ\^{e}te du client le message post\'{e}. Elle ajoute \`{a} la liste des messages une nouvelle instance de \textcode{TCMessage}.

\begin{displaycode}{smalltalk}
TCServer >> addMessage: aRequest
	messagesQueue add: (TCMessage from: (aRequest at: #sender) text: (aRequest at: #text)).
\end{displaycode}

La m\'{e}thode \textcode{messageCount} retourne le nombre de messages re\c{c}us.

\begin{displaycode}{smalltalk}
TCServer >> messageCount
	^ messagesQueue size
\end{displaycode}

La m\'{e}thode \textcode{messageFrom:} retourne la liste des messages re\c{c}us par le serveur depuis l'index indiqu\'{e} par le client. Les messages sont retourn\'{e}s au client 
sous la forme d'une chaine de caract\`{e}res. Ce point sera d\'{e}finitivement \`{a} am\'{e}liorer. 

\begin{displaycode}{smalltalk}
TCServer >> messagesFrom: request
	^ messagesQueue formattedMessagesFrom: (request at: #id)  
\end{displaycode}

Nous en avons fini avec le server. Nous pouvons maintenant le tester un peu. Commen\c{c}ons par le lancer : 

\begin{displaycode}{smalltalk}
	TCServer startOn: 8181
\end{displaycode}

Maintenant nous pouvons soit v\'{e}rifier avec un navigateur web (figure \ref{running}), soit \`{a} l'aide du client/serveur Zinc disponible par d\'{e}faut dans Pharo.

\begin{displaycode}{smalltalk}
ZnClient new url: 'http://localhost:8181/messages/count' ; get
\end{displaycode}

Les amateurs du shell peuvent \'{e}galement utiliser la commande curl 

\begin{displaycode}{smalltalk}
	curl http://localhost:8181/messages/count
\end{displaycode}


\begin{figure}

\begin{center}
\includegraphics[width=0.8\textwidth]{figures/running.png}\caption{Testons le serveur.\label{running}}\end{center}
\end{figure}


Nous pouvons aussi ajouter un message de la mani\`{e}re suivante : 

\begin{displaycode}{smalltalk}
ZnClient new 
	url: 'http://localhost:8181/messages/add';
	formAt: 'sender' put: 'olivier';
	formAt: 'text' put: 'Super cool ce tinychat' ; post
\end{displaycode}
\section{Le client}
Maintenant, nous pouvons nous concentrer sur la partie client de TinyChat. Le client se compose de deux classes:

\begin{itemize}
\item \textcode{TinyChat} est la classe contenant la logique m\'{e}tier (connexion, envoi et r\'{e}ception des messages),
\item \textcode{TCConsole} est une classe d\'{e}finissant l'interface graphique.
\end{itemize}

La logique du client est la suivante:

\begin{itemize}
\item Au lancement du client, celui-ci demande au serveur l'index du dernier message re\c{c}u,
\item Toutes les deux secondes, le client se connecte au serveur pour lire les messages \'{e}chang\'{e}s depuis sa derni\`{e}re connexion. Pour cela, il transmet au serveur l'index du dernier message dont il a eu connaissance.
\end{itemize}

De plus, lorsque le client transmet un message au serveur, il en profite pour \'{e}galement lire les messages \'{e}chang\'{e}s depuis sa derni\`{e}re connexion.
\subsubsection{La classe TinyChat}
Nous cr\'{e}ons la classe \textcode{TinyChat} dans le package \textcode{TinyChat-client}. 

\begin{displaycode}{smalltalk}
Object subclass: #TinyChat
	instanceVariableNames: 'url login exit messages console lastMessageIndex'
	classVariableNames: ''
	category: 'TinyChat-client'
\end{displaycode}

Cette classe d\'{e}finit les variables suivantes:

\begin{itemize}
\item url contient l'url HTTP permettant au client de se connecter au serveur,
\item login est une cha\^{i}ne de caract\`{e}res identifiant le client,
\item messages est une variable d'instance contenant les messages lus par le client,
\item lastMessageIndex est le num\'{e}ro du dernier message lu par le client,
\item exit est une valeur bool\'{e}enne. Tant que cette valeur est vraie, le client se connecte \`{a} intervalle r\'{e}gulier au serveur pour lire les messages \'{e}chang\'{e}s depuis sa derni\`{e}re connexion,
\item console pointe sur l'instance de la console graphique permettant \`{a} l'utilisateur de saisir et de consulter les messages.
\end{itemize}

Nous initialisons les variables qui le n\'{e}cessitent dans la m\'{e}thode \textcode{initialize} suivante.

\begin{displaycode}{smalltalk}
TinyChat >> initialize
	super initialize.
	exit := false.
	lastMessageIndex := 0.
	messages := OrderedCollection new.
\end{displaycode}
\subsubsection{D\'{e}finir les commandes HTTP}
Nous d\'{e}finissons les m\'{e}thodes pour communiquer avec le serveur. Elles respectent le protocole HTTP.

Deux m\'{e}thodes permettent de formater la requ\^{e}te. L'une n'a pas d'argument et 
permet de construire les requ\^{e}tes \textcode{/messages/add} et \textcode{/messages/count}. L'autre a un 
argument qui est utilis\'{e} pour la lecture des messages \`{a} partir d'une position.

\begin{displaycode}{smalltalk}
TinyChat >> command: aPath
	^'{1}{2}' format: { url . aPath }

TinyChat >> command: aPath argument: anArgument
	^'{1}{2}/{3}' format: { url . aPath . anArgument asString }
\end{displaycode}

Il suffit ensuite de d\'{e}finir les trois commandes HTTP du client:

\begin{displaycode}{smalltalk}
TinyChat >> cmdLastMessageID
	^ self command: '/messages/count'

TinyChat >> cmdNewMessage
	^self command: '/messages/add'

TinyChat >> cmdMessagesFromLastIndexToEnd
	"Returns the server messages from my current last index to the last one on the server."
	^ self command: '/messages' argument: lastMessageIndex
\end{displaycode}
\subsubsection{G\'{e}rer les op\'{e}rations du client}
Nous avons besoin d'\'{e}mettre ces commandes et de pouvoir r\'{e}cup\'{e}rer des informations \`{a} partir du serveur.
Pour cela, nous d\'{e}finissons deux m\'{e}thodes. La m\'{e}thode \textcode{readLastMessageID} retourne l'index du dernier 
message re\c{c}u par le serveur.

\begin{displaycode}{smalltalk}
TinyChat >> readLastMessageID
	| id |
	id := (ZnClient new url: self cmdLastMessageID; get) asInteger.
	id = 0 ifTrue: [ id := 1 ].
	^ id
\end{displaycode}

La m\'{e}thode \textcode{readMissingMessages} ajoute les derniers messages re\c{c}us par le serveur \`{a} la 
liste des messages connus par le client. Cette m\'{e}thode retourne le nombre de messages r\'{e}cup\'{e}r\'{e}s.

\begin{displaycode}{smalltalk}
TinyChat >> readMissingMessages
	"Gets the new messages that have been posted since the last request."
	| response receivedMessages |
	response := (ZnClient new url: self cmdMessagesFromLastIndexToEnd; get).
	^ response 
		ifNil: [ 0 ]
		ifNotNil: [  
			receivedMessages := response subStrings: (String crlf).
			receivedMessages do: [ :msg | messages add: (TCMessage fromString: msg) ].
			receivedMessages size.
		].
\end{displaycode}

Nous sommes pr\^{e}t \`{a} d\'{e}finir le comportement de rafraichissement du client avec la  m\'{e}thode \textcode{refreshMessages}.
Elle utilise un processus l\'{e}ger pour lire \`{a} intervalle r\'{e}gulier les messages re\c{c}us par le serveur. Le d\'{e}lai est fix\'{e} \`{a} deux secondes.
Le message \textcode{fork} envoy\'{e} \`{a} un bloc (une fermeture lexical en Pharo) ex\'{e}cute ce bloc dans un processus l\'{e}ger. La logique est
de boucler tant que le client ne sp\'{e}cifie pas que veut s'arr\^{e}ter via la variable \textcode{exit}. 
L'expression \textcode{(Delay forSeconds: 2) wait} suspend l'ex\'{e}cution du processus l\'{e}ger dans lequel elle se trouve pendant un certain
 nombre de secondes. 

\begin{displaycode}{smalltalk}
TinyChat >> refreshMessages
	[  
		[ exit ] whileFalse: [  
			(Delay forSeconds: 2) wait.
			lastMessageIndex := lastMessageIndex + (self readMissingMessages).
			console print: messages.
		]
	] fork
\end{displaycode}

La m\'{e}thode \textcode{sendNewMessage:} poste le message de l'utilisateur au serveur.

\begin{displaycode}{smalltalk}
TinyChat >> sendNewMessage: aMessage
	^ ZnClient new
		url: self cmdNewMessage;
		formAt: 'sender' put:  (aMessage sender);
		formAt: 'text' put: (aMessage text);
		post
\end{displaycode}

Cette m\'{e}thode est utilis\'{e}e par la m\'{e}thode \textcode{send:} qui re\c{c}oit en param\`{e}tre le texte saisi par l'utilisateur. 
La chaine de caract\`{e}res est alors convertie en une instance de \textcode{TCMessage}. Le message est envoy\'{e}. 
Le client met \`{a} jour l'index du dernier message connu et d\'{e}clenche l'affichage du message dans l'interface graphique.

\begin{displaycode}{smalltalk}
TinyChat >> send: aString
	"When we send a message, we push it to the server and in addition we update the local list of posted messages."
	
	| msg |
	msg := TCMessage from: login text: aString.
	self sendNewMessage: msg.
	lastMessageIndex := lastMessageIndex + (self readMissingMessages).
	console print: messages.
\end{displaycode}

La d\'{e}connexion du client est g\'{e}r\'{e}e par la m\'{e}thode \textcode{disconnect} qui envoie un message au serveur pour signaler le 
d\'{e}part de l'utilisateur et met fin \`{a} la boucle de r\'{e}cup\'{e}ration p\'{e}riodique des messages.

\begin{displaycode}{smalltalk}
TinyChat >> disconnect
	self sendNewMessage: (TCMessage from: login text: 'I exited from the chat room.').
	exit := true
\end{displaycode}
\subsubsection{Fixer les param\`{e}tres du client}
Pour initialiser les param\`{e}tres de connexion, on d\'{e}finit une m\'{e}thode de class \textcode{TinyChat class\textgreater{}\textgreater{}connect:port:login:}. 
Cette m\'{e}thode permet de se connecter de la mani\`{e}re suivante : \textcode{TinyChat connect: 'localhost' port: 8080 login: 'username'}

\begin{displaycode}{smalltalk}
TinyChat class >> connect: aHost port: aPort login: aLogin

	^ self new
		host: aHost port: aPort login: aLogin;
		start
\end{displaycode}

Le code appelle la m\'{e}thode \textcode{host:port:login:}. Cette m\'{e}thode met \`{a} jour la variable d'instance \textcode{url} en construisant l'URL et 
en affectant le nom de l'utilisateur \`{a} la variable d'instance \textcode{login}.

\begin{displaycode}{smalltalk}
TinyChat >> host: aHost port: aPort login: aLogin
	url := 'http://' , aHost , ':' , aPort asString.
	login := aLogin
\end{displaycode}

La m\'{e}thode \textcode{start} envoie un message au serveur pour pr\'{e}senter l'utilisateur, r\'{e}cup\`{e}rer l'index du dernier message re\c{c}u par le serveur 
et mettre \`{a} jour la liste des messages connus par le client. 
C'est \'{e}galement cette m\'{e}thode qui initialise l'interface graphique de l'utilisateur. Une \'{e}volution pourrait \^{e}tre de d\'{e}coreller le mod\`{e}le de son interface graphique en utilisant 
une conception bas\'{e}e sur des \'{e}venements.

\begin{displaycode}{smalltalk}
TinyChat >> start
	console := TCConsole attach: self.
	self sendNewMessage: (TCMessage from: login text: 'I joined the chat room').
	lastMessageIndex := self readLastMessageID.
	self refreshMessages.
\end{displaycode}
\subsubsection{Cr\'{e}ation de l'interface graphique}
L'interface graphique est compos\'{e}e d'une fen\^{e}tre contenant une liste et un champ de saisie comme montr\'{e} dans la figure \ref{tinychatclient}. 

\begin{displaycode}{smalltalk}
ComposableModel subclass: #TCConsole
	instanceVariableNames: 'chat list input'
	classVariableNames: ''
	category: 'TinyChat-client'
\end{displaycode}

La variable d'instance \textcode{chat} est une r\'{e}f\'{e}rence \`{a} une instance de la classe \textcode{TinyChat} et n\'{e}cessite uniquement un accesseur en \'{e}criture. 
Les variables d'instance \textcode{list} et \textcode{input} dispose chacune d'un accesseur en lecture. Ceci est impos\'{e} par Spec le constructeur d'interface.

\begin{displaycode}{smalltalk}
TCConsole >> input
	^ input

TCConsole >> list
	^ list

TCConsole >> chat: anObject
	chat := anObject
\end{displaycode}

L'interface graphique a un titre pour la fen\^{e}tre. Pour le d\'{e}finir, il faut \'{e}crire une m\'{e}thode \textcode{title}.

\begin{displaycode}{smalltalk}
TCConsole >> title
	^ 'TinyChat'
\end{displaycode}

La m\'{e}thode de classe \textcode{TCConsole class\textgreater{}\textgreater{}attach:} re\c{c}oit en argument l'instance du client de chat avec lequel l'interface graphique 
va \^{e}tre utilis\'{e}e. Cette m\'{e}thode d\'{e}clenche l'ouverture de la fen\^{e}tre et met en place l'\'{e}v\'{e}nement g\'{e}rant la fermeture de celle ci et donc, 
provoquant la d\'{e}connexion du client.
 

\begin{displaycode}{smalltalk}
TCConsole class >> attach: aTinyChat
	| window |
	window := self new chat: aTinyChat.
	window openWithSpec whenClosedDo: [ aTinyChat disconnect ].
	^ window
\end{displaycode}

La m\'{e}thode \textcode{TCConsole class\textgreater{}\textgreater{}defaultSpec} d\'{e}finit la mise en page des composants contenus dans la fen\^{e}tre. Ici nous avons une colonne avec une liste
et un champ de saisie plac\'{e} juste en dessous.

\begin{displaycode}{smalltalk}
TCConsole class >> defaultSpec
	<spec: #default>

	^ SpecLayout composed
		newColumn: [ :c | 
			c add: #list; add: #input height: 30 ]; yourself
\end{displaycode}

La m\'{e}thode \textcode{initializeWidgets} sp\'{e}cifie la nature et le comportement des composants graphiques. Ainsi le \textcode{acceptBlock:} permet de d\'{e}finir l'action \`{a} ex\'{e}cuter lorsque le texte
est entr\'{e} dans le champ de saisie. Ici nous l'envoyons \`{a} client et nous le vidons.

\begin{displaycode}{smalltalk}
TCConsole >> initializeWidgets

	list := ListModel new.
	input := TextInputFieldModel new 
		ghostText: 'Type your message here...';
		enabled: true;
		acceptBlock: [ :string |  
			chat send: string. 
			input text: '' ].
	self focusOrder add: input.
\end{displaycode}

La m\'{e}thode \textcode{print} affiche les messages re\c{c}us par le client en les affectant au contenu de la liste.

\begin{displaycode}{smalltalk}
TCConsole >> print: aCollectionOfMessages
	list items: (aCollectionOfMessages collect: [  :m |  m printString ])
\end{displaycode}

Notez que cette m\'{e}thode est invoqu\'{e}e par la m\'{e}thode \textcode{refreshMessages} et que changer tous les \'{e}l\'{e}ments de la liste
\`{a} chaque ajout d'un nouveau messages est peu \'{e}l\'{e}gant mais l'exemple se veut volontairement simple. 


\begin{figure}

\begin{center}
\includegraphics[width=0.6\textwidth]{figures/messages.png}\caption{Acces direct au server.\label{messages}}\end{center}
\end{figure}


Voil\`{a} vous pouvez maintenant chatter avec votre serveur. 
\section{Conclusion}
Nous avons montr\'{e} que la cr\'{e}ation d'un serveur REST est extr\^{e}mement simple avec Teapot. La d\'{e}finition de TinyChat donne un cadre ludique \`{a} l'exploration 
de la programmation en Pharo et nous esperons que vous avez appr\'{e}ci\'{e} cette ballade. TinyChat est une petite application que nous avons d\'{e}velopp\'{e} de mani\`{e}re tr\`{e}s 
simple afin  de vous permettre de l'\'{e}tendre et d'exp\'{e}rimenter. 
Voici une liste d'am\'{e}liorations : gestion parcimonieuse des ajouts d'\'{e}l\'{e}ments dans la liste graphique, gestion d'acc\`{e}s concurrents dans la collection sur le serveur (en effet, si le serveur pouvait recevoir des requ\^{e}tes concurrentes la structure de donn\'{e}e utilis\'{e}e n'est pas ad\'{e}quate), gestion des erreurs de connexion,
rendre les clients robustes \`{a} la fermeture du serveur, obtenir la liste des personnes connect\'{e}es, pouvoir d\'{e}finir le d\'{e}lai de r\'{e}cup\'{e}ration des messages, utiliser JSON pour le transport des messages, afficher le nom de la personne connect\'{e}e dans la fen\^{e}tre. 
Le projet est disponible \`{a} l'adresse \url{http://www.smalltalkhub.com/#!/~olivierauverlot/TinyChat}. A vous de jouer!


\ifx\wholebook\relax\else
\end{document}
\fi