La Semana 1 construímos un core de servidor de chat que hacía bastante bien su trabajo. Ahora el paso lógico es:
- Integrar este core con un servidor HTTP
- Crear una User Interface (UI) bonita y amigable
[Servidor HTTP]
El primer punto, que al principio no acababa de ver claro, ha sido muy simple. Lo primero de todo era encontrar un servidor HTTP para Erlang. Mis requisitos eran:
- Escrito 100% en Erlang. Para no liarnos con otras tecnologías
- Que pueda soportar mucha concurrencia. Por motivos obvios en la aplicación que queremos crear.
- Con un sistema muy simple de hacer dispatch de llamadas, con controladoras y todo el tinglado. Quería algo que ya te lo diese un poco mascado, pero que fuera altamente personalizable.
El servidor web MochiWeb cumple con todos los requisitos anteriores. El trabajo principal ha sido crear un pequeño dispacher para cada una de las llamadas, para poder ofrecer una interfície restful simple, limpia y clara pero que nos sirva para nuestros propósitos. Para saber exactamente cómo está implementado, que se mire el código (mas sobre esto al final), pero para dar una idea general os enseño la API REST que he creado:
- Acción: /login Método: GET. Parámetros: N/A. Explicación: Renderiza la pantalla de login
- Acción: /login Método: POST. Parámetros: username, password. Explicación: Loguea el usuario, en caso de que los datos sean correctos, mediante el establecimiento de una cookie.
- Acción: /logout Método: GET. Parámetros: N/A. Explicación: Lo obvio
- Acción: /msg Método: GET. Parámetros: N/A. Explicación: Éste es el mas importante de todos. Está pensado para ser llamado por AJAX y manetener la conexión abierta durante 20 segundos. Recibe como respuesta un JSON con los mensajes que puedan haber enviado enviado otros usuarios durante estos 20 segundos, la notificación de que otro usuario se ha logueado, etc... Más sobre esto, luego.
- Acción: /msg Método: POST. Parámetros: msg, to Explicación: Envia el mensaje msg al usuario to. ¿Fácil, no?
- Acción: /users_list Método: GET. Parámetros: N/A. Explicación: Devuelve un JSON con el listado de amigos conectados en este momento.
¿Simple, verdad?
La acción mas interesante es /msg per método GET. Vamos a explicarla un poquito mas. Como comentaba mas arriba, esta llamada devuelve un MIME-type application/json. Nada de XML ni líos, JSON nos va perfecto para este caso, ya que esto lo recibe la parte cliente y tiene que interpretarse en Javascript. Sabiendo que JSON significa JavaScript Object Notation, queda claro que para este caso ésta es la elección ideal. Esta acción está pensado para ser llamado por AJAX, pero también puede ser llamada por el método tradicional sin problemas. Cuando se cree la conexión, ésta permanecerá abierta durante 20 segunos. Durante este tiempo, si se produce algún evento en la parte servidor como por ejemplo recicibir un mensaje, se mandará el mensaje hacia el usuario a través de esta conexión. Si no hay nada que notificar en 20 segundos, se cierra la conexión y se espera que el cliente abra otra inmediatamente después, esperando así indefinidamente a recibir algún tipo de mensaje.
¿Qué tipo de información devuelve éste método? Pues información que tenga que ver con cambios de estado, mensajes de chat, notificaciones, etc... Veamos los JSON's de posibles resultados:
- {"action":"message", "user":"inedit00", "msg": "Hello world!"}
- {"action": "friend_logged", "name": "foo"}
- {"action": "friend_unlogged", "name": "bar"}
- ....
Como podeis ver la interfície es muy clara y legible. El action determina qué tipo de mensaje es, y cada mensaje tiene los campos con la información necesaria. Procesar esto en JS es prácticamente un chiste. Mirad, así, en pseudo-javascript y para que os hagáis una idea:
MessageHandler = {
message: function(request) { alert(request.msg) },
friend_logged: function(request) {alert("The user " + request.name + "logged in")},
.....
}
var process_http_response = function(html) {
json = JSON.decode(html)
MessageHandler[json["action"]](json)
}
nada... un juego de niños. La ventaja de utilizar algo así es que para añadir una acción más, simplemente hay que mandar un mensaje distinto y crear su handler en el código JS. Obviamente el código JS está pensado para ignorar todos los mensajes con un action para los que no tiene handler.
Total, que con todo esto ya tenemos una manera de mandar mensajes a otros usuarios en el sistema des de la parte cliente (JavaScript) y que sean recibidos por otros usuarios mediante HTTP. Genial.
[User Interface]
Bueno, esto simplemente es HTML+CSS+JS. Aquí no hay mucho que contar. Si alguien se lo quiere mirar que vaya al repositorio y mire el código fuente de todo el percal (es poquito y simple). Os dejo con el número de líneas que he usado para cada una de las cosas, y un par de capturas de pantalla, y arreando....
- Página principal /index 37 líneas de HTML
- Página de login /login 31 líneas de HTML
- Fichero con todos el CSS's del proyecto: 98 líneas
- Fichero con todo el JS del proyecto 195 líneas. Incluye la creación, cambio de tamaño/posición de las ventanas de chat, control de las conexiones por AJAX al servidor y los handlers que procesan las respuestas (mensajes nuevos, usuarios conectados/desconectados, etc....)....
Nhá... ha quedado todo muy pequeñito y limpio. Por supuesto nada de estilos inline, ni CSS en bloques style dentro de los ficheros HTML, ni Javascript inline en atributos HTML (¡aberración!) ni demás guarradas varias en el código. Sabéis que si hacéis estas cosas vais a ser sodomizados por Diox durante toda la eternidad, ¿verdad?
En el HTML hay dos imports, uno al CSS y otro al fichero JS y palante toda. Y mas o menos el tema luce así (que siiiiiii, que sé que todo lo anterior que os he contado os la sudaaaa, que coméis por los ojos!! ):
Ahora mismo solo hay un usuario conectado con el que pueda hablar (yo mismo en otro navegador ¬¬'). En las ventana de chat soporta modificación de tamaño, ponerla donde quieras en la pantalla, cerrarla y puedes tener abiertas tantas conversaciones de chat como quieras (siempre que tengas mas amigos, obviamente). Para ser una primera versión sacada en una tarde (HTML+CSS), estoy muy contento con el resultado.
Y nada.... esto es básicamente todo lo que tiene que ver con UI. Los ficheros interesantes del repositorio que tienen que ver con UI son:
- /templates/index.dtl
- /temlates/login.dtl
- /priv/www/js/gaab.js
- /priv/www/css/gaab.css
[Repositorio]
¿Repositorio? ¿Quién dijo repositorio? Pues claro que si, hombre! Si trabajas con código y no lo versionas es que te gusta vivir peligrosamente. Seguramente tus hobbies puedan ser hacer puenting sin cuerda, hacer la danza de pescado crudo en playas infestadas de tiburones en Madagascar o intentar ligar con la chica buenorra con el novio culturista.... a saber.
Todal, que tengo todo este código subido a un repositorio de bitbucket para uso y disfrute de todo quien quiera verlo:
y esto es todo... quien quiera preguntar alguna cosa, sugerir alguna otra, pedirme que explique algo un poco mas o tenga curiosidad en lo que sea: que me mande una caja de cerveza...... digooooooooo..... que deje un comentario!
Un saludo!