Desarrollando un chat con Elixir y Phoenix

The Phoenix is coming!!!

En esta entrada mostraremos lo sencillo que es crear una aplicación en tiempo real usando Elixir y unos de los frameworks más populares y con gran proyección del ecosistema de Erlang/Elixir: Phoenix.

Hace un par de días, Chris McCord el creador de Phoenix realizó un par de aclaraciones muy importantes:

  1. Los canales de Phoenix (Phoenix Channels) son una abstracción de alto nivel sobre WebSockets. En Phoenix se crean “canales” aislados y concurrentes en la conexión WebSocket entrante. Se monitorean esos canales y los clientes son notificados sobre todas las posibles interacciones, incluyendo posibles errores. Esto contribuye a un mejor uso de la memoria y a un mayor rendimiento.
  2. Los canales de Phoenix corren en un sistema distribuido pubsub. Lo que quiere decir que al escalar la aplicación no tendrás que pensar en implementaciones de nodos locales y demás dolores de cabeza. En Phoenix tienes un sistema distribuido “out of the box” o por defecto.

Dicho todo esto, hoy crearemos el Hello World de las aplicaciones en tiempo real: un chat.

Te sorprenderás con lo simple que es, ya que en menos de 10 minutos tendrás un chat funcional, el cual funciona en memoria y no persiste los mensajes a una base de datos (aunque no es muy complicado de hacer).

Asumo que tienes Elixir instalado, de no ser así, visita esta página donde hay instrucciones de como instalar el lenguaje para los diferentes sistemas operativos. También asumo que tienes instalado Phoenix, puedes leer instrucciones de como hacerlo en su página.


Generando nuestra aplicación

Phoenix al igual que el resto de aplicaciones escritas en Elixir, hace uso de Mix, la cual es una herramienta que provee tareas para crear, compilar, probar y manejar las dependencias de aplicaciones escritas en Elixir, entre muchas otras funcionalidades, además incluye muchas convenciones que son aceptadas por la mayoría de desarrolladores de la comunidad.

Para generar una nueva aplicación de Phoenix basta con correr el siguiente comando en una sesión del terminal:

$ mix phoenix.new loopa_chat

Si te pide introducir y/n solo escribe y, esto hará que descargue los modulos de node necesarios para que brunch funcione.

El comando generará la estructura de nuestro proyecto, también puedes pasar parametros opcionales al comando anterior, de la siguiente manera:

$ mix phoenix.new loopa_chat --no-ecto --no-brunch --no-html

Phoenix usa Brunch para compilar nuestros assets estáticos por defecto, sin embargo puedes utilizar otras herramientas, desde module bundlers hasta task runners, como Webpack, Grunt, Gulp, etc.

La estructura de una aplicación en Phoenix luce de la siguiente manera:

Estructura de la aplicación

La mayor parte del tiempo la pasaremos trabajando en el directorio “web”, ya que allí es donde residen nuestros modelos, controladores, vistas, plantillas, canales y archivos estáticos (JS, CSS e imágenes).

Antes de comenzar con el desarrollo de la aplicación, comprueba que esta funciona, ejecutando el siguiente comando:

$ iex -S mix phoenix.server

Tus archivos serán compilados y posteriormente podrás visitar tu página en la dirección http://localhost:4000; si notas algún problema, asegurate de que tu base de datos esta encendida y las credenciales son correctas. Puedes cambiarlos en todo momento editando los archivos en el directorio config. Si te dice que no existe una base de datos, intenta crearla con el comando

$ mix ecto.create

Si todo sale bien, podrás ver la siguiente pantalla en http://localhost:4000

¡Nuestra aplicación funciona, hurra!

Por cierto, deberías de ver algo como esto en la consola:

[debug] Processing by LoopaChat.PageController.index/2
Parameters: %{}
Pipelines: [:browser]
[info] Sent 200 in 453µs

453µs!!, así es, Phoenix envía respuestas en microsegundos !Hablame de velocidad!

Elixir cuenta con LiveReload, por lo cual tus cambios serán reflejados en tu aplicación a una velocidad casi instantánea; para probarlo, vamos a editar un template, abre el archivo web/templates/page/index.html.eex y cambia todo su contenido por el siguiente:

<h1>¡Hola mundo! - Desde Phoenix</h1>

Navega de nuevo al sitio y verás que ya ha cambiado:

LiveReload en acción

LiveReload no solo funciona con archivos HTML, Brunch recompilara tus archivos JavaScript y CSS de igual manera, y Elixir compilara el resto de tus archivos en segundos.

Una vez más, edita el archivo index.html.eex:

<div id="messages"></div>
<input id="name" type="text" placeholder="name"></input>
<input id="chat-input" type="text" placeholder="message"></input>

Esta será nuestra plantilla principal, desde aquí podremos enviar y visualizar mensajes de nuestro chat.

Si te fijas, Phoenix creo automáticamente un archivo para manejar nuestros canales en web/channels/user_socket.ex, abrelo y elimina el comentario en la linea “channel: “room:* ….”, debería verse así:

defmodule LoopaChat.UserSocket do
use Phoenix.Socket
## Channels
channel "room:*", LoopaChat.RoomChannel
.
.
.

Los puntos indican que hay más código hacia abajo.

Ahora, crea el archivo room_channel.ex en el mismo directorio:

Creamos un nuevo módulo, usamos los canales de Phoenix y actuamos respecto a 3 eventos:

  1. join: si el usuario se conecta a nuestro socket sin problemas, retornamos el átomo :ok y la conexión al socket.
  2. handle_in: recibe información del cliente al servidor.
  3. handle_out: envía información a los clientes conectados.

Ahora, en el archivo web/static/js/app.js busca la linea

// import socket from “./socket”

Y elimina su comentario, de manera que importa el archivo socket en el mismo directorio.

Abre el archivo socket y al final edita el canal del socket, de manera que quede algo así:

const channel = socket.channel("room:lobby", {})

Si navegamos a nuestra página e inspeccionamos la consola deberíamos de ver lo siguiente:

¡Una conexión éxitosa!

Ahora, añadiremos jQuery para crear nuestro chat, en el archivo web/templates/layout/app.html.eex añade un CDN a jQuery, el mio queda así:

.
.
.
</div> <!-- /container -->
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
<script src="<%= static_path(@conn, "/js/app.js") %>"></script>
.
.
.

Ahora en nuestro archivo socket.js haremos un par de modificaciones:

const channel = socket.channel("room:lobby", {})
const name = $("#name")
const chatInput = $("#chat-input")
const messagesContainer = $("#messages")

Usando jQuery, seleccionaremos partes de nuestro documento HTML para luego enviar datos de ellos e insertar nuestros mensajes, para esto tenemos que crear una función que los envíe:

chatInput.on("keypress", event => {
if(event.keyCode === 13) {
channel.push("new_msg", {
name: name.val(),
body: chatInput.val()
})
chatInput.val("")
}
})

Cuando el usuario presione la tecla Enter (Return) enviará al canal un mensaje que contiene su nombre y el contenido del mismo, luego vaciamos el input para que pueda escribir un nuevo mensaje sin problemas.

Nuestros mensajes no se ven aún, para esto tenemos que crear otra función que reaccione a los nuevos mensajes en nuestro canal (new_msg).

channel.on("new_msg", payload => {
messagesContainer.append(
`<strong>${payload.name || "Anónimo"}:</strong> ${payload.body}`
)
})

Prueba enviando mensajes ahora, deberían de mostrarse, abre varias ventanas y observa lo simple que fue implementar un chat en Phoenix.

Como última característica del chat y para hacerlo más especial, añadiremos soporte markdown a nuestros mensajes, para ello utilizaremos Remarkable, así que añade un CDN a este, en el archivo app.html.eex justo debajo de jQuery:

<script src="https://npmcdn.com/remarkable@1.6.2/dist/remarkable.min.js"></script>

Y ahora, debemos usarlo en nuestro archivo socket.js:

Prueba escribiendo markdown en tu mensaje ahora, bastante genial por tan poco trabajo. Te invito a que apliques estilos a tu aplicación, yo saque el artista interior que no soy y termine con algo así:

Mi versión del chat

Puedes ver la aplicación en acción en https://guarded-dusk-93351.herokuapp.com/ y puedes encontrar el código de este pequeño tutorial en el repositorio en https://github.com/Oxyrus/LoopaChat


Escrito por Andrés Pérez

Valoramos tus opiniones. Por favor, déjanos tus comentarios y preguntas.

Si consideras que este artículo es útil y puede ayudar a otros, te invitamos a que lo compartas a través de las redes sociales y te suscribas a nuestro canal de Youtube para videos informativos. ¡Gracias!