Notxor tiene un blog

Defenestrando la vida

Aprender elixir

Notxor
2021-02-15

Estos días de atrás abandoné el aprendizaje de Rust por motivos ajenos al propio lenguaje o a mi dura mollera. Mi tiempo de estudio se ha quedado vacío y hay unas horas al día que podría aprovechar en procrastinar como todo el mundo o buscarme algo distinto que aprender. Después de pensarlo un poco aparecieron un par de puntos que me gustaría poder aprender a mi ritmo:

  1. elixir
  2. phoenix

Parece que la red social pleroma está realizada con elixir y montada sobre un framework muy parecido a ruby on rails, porque se diseñó con él en mente, que se llama phoenix. Si a eso le añadimos que elixir puede ejecutar e integrarse con librerías hechas en erlang, y que erlang es un lenguaje que me encanta, no he podido resistir la curiosidad. Además creo que tengo un proyecto por finalizar donde ambas herramientas pueden ser de gran ayuda.

Primeras impresiones

De momento sólo he echado un ojo por encima de manera rápida a elixir y sus satélites. Llamo satélites a esos programas que vienen con los lenguajes para formar una especie de ecosistema simbiótico entre distintas aplicaciones. Con erlang se trabajaba con rebar3 ─aunque yo era más partidario de utilizar make, como el viejuno que soy~, en Rust se utilizaba cargo y parece que en elixir se utiliza mix. Ahora parece que todos los lenguajes tienen su sistema de paquetes y herramienta para instalar librerías y dependencias.

Por resumir muy rápido mis primeras impresiones sobre el lenguaje, elixir, me está pareciendo un erlang con una sintaxis muy parecida a Ruby. No he profundizado mucho pero, a primera vista, mantiene todas esas cosas que me gustan de erlang, también permite utilizar OTP pero con sintaxis elixir y algunas características más especiales que tengo que asimilar todavía.

Preparando las herramientas

Compilando elixir

El lenguaje depende de tener instalado también erlang, y esa dependencia la tengo cumplida, aunque un poco a mi manera: como ya conté en este blog, yo utilizo una versión compilada, instalada en mi directorio personal de programas opcionales. Básicamente, cualquier cosa que necesito para hacer algunas pruebas, o que no está en un paquete oficial para OpenSuse lo compilo en un directorio local para mi usuario en el directorio ~/opt y si es ejecutable, va a ~/opt/bin, y en él también hay librerías cargadas en ~/opt/lib/, etc.

El camino alternativo y fácil sería instalar los paquetes que vienen para OpenSuse, sin embargo, mi enfermiza tendencia a complicarme la vida hacen que me haya compilado estas herramientas.

Si has compilado erlang, como yo, no te costará nada compilar también elixir. Sin embargo, al descargar el código fuente del lenguaje de su repositorio git, me encuentro que no hay un configure que echarse a la cara. La alternativa es modificar los path en el Makefile. Esperaba tener que modificar toda aparición de /usr/local/ por mi directorio de opcionales, sin embargo, me encontré que el fichero, ya en la primera línea del mismo comienza por:

PREFIX ?= /usr/local

Y yo la cambié por:

PREFIX ?= /home/notxor/opt

Si os fijáis he puesto el path absoluto, porque el relativo ~ no me funcionó correctamente. Una vez modificado el Makefile el proceso es el habitual:

make
make install

Eso compila e instala el lenguaje. Pero si intentamos generar también la documentación nos saltará un error que nos dice que falta una herramienta, ex_doc, para compilarlo. Si queremos tener también la documentación tenemos que instalar alguna cosa más. Suponiendo que estamos aún en el directorio de los fuentes de elixir podemos ejecutar lo siguiente:

cd ..
git clone git://github.com/elixir-lang/ex_doc.git
cd ex_doc
../elixir/bin/mix do deps.get, compile

Al terminar la compilación de esa herramienta, podemos volver al directorio de los fuentes de elixir y terminar con la compilación de la documentación:

cd ../elixir
make docs                    # generar las páginas HTML
make docs DOCS_FORMAT=epub   # generar los documentos EPUB

Como digo, la instalación de la documentación y la herramienta que necesita para generarse es totalmente opcional y podemos prescindir de ella para trabajar. Al fin y al cabo, la documentación está accesible desde la Internet sin necesidad de ocupar espacio en nuestro disco duro.

Instalando PostgreSQL

¿Por qué PostgreSQL? La respuesta es sencilla: la base de datos por defecto que utiliza phoenix es esa. La respuesta más larga implica, o tiene que ver, que siempre me ha gustado esa base de datos más que la más popular MySQL (ahora mariadb). Me vais a permitir que deje las explicaciones para otro momento y no alargar el artículo en demasía. Si al contrario, os gusta más MySQL también se puede utilizar esa abase de datos.

Algunas consideraciones iniciales: No necesito un servidor de bases de datos corriendo continuamente, tan sólo quiero hacer algunas pruebas locales y temporales, por lo tanto, decidí no instalar el paquete de que viene con OpenSuse y hacerlo todo más manual... ¿qué tal si lo compilamos?

Me he bajado el código fuente de la web de PostgreSQL metido en un archivo comprimido... ni siquiera he querido hacerlo con un respositorio git para mantenerlo actualizado, porque no sé si durará más allá de las pruebas que voy a hacer. Al desempaquetar el código fuente vemos que se genera con las habituales herramientas de GNU/Linux:

./configure --prefix=~/opt
make
make install
mkdir ~/opt/data

Lo hecho hasta este momento es decirle a configure que utilice como directorio destino mi directorio personal de herramientas totalmente opcionales ~/opt y que ahí se instalará todo. Por ejemplo, los binarios irán al directorio ~/opt/bin. Para arrancar el servidor y pararlo me he hecho un pequeño script que me facilitará la vida:

#! /usr/bin/bash

pg_ctl -D /home/notxor/opt/data -l /home/notxor/opt/data/logfile $1

Ese contenido lo he guardado en ~/opt/bin/base-de-datos y como el directorio está en el path tan sólo tengo que llamar a:

base-de-datos start

cuando quiero levantar la base de datos y a

base-de-datos stop

cuando quiero parar el servidor. Para observar las conexiones y acciones sobre la base de datos, se activa el fichero ~/opt/data/logfile para facilitarme la información necesaria para la administración de la base de datos. Obsérvese que lo de start o stop está en la sustitución del parámetro $1 del script.

Para probar si todo funciona como se espera, voy a activar el servidor de base de datos, crear una base de datos y conectarme a ella:

base-de-datos start
createdb test
psql test

Si todo ha ido bien debería aparecernos un prompt que nos indica que estamos conectados a la base de datos test y podemos hacer algunas pruebas más, tanto con comandos de PostgreSQL como de instrucciones SQL:

test=# \dS+
test=# select * from pg_user;
 usename | usesysid | usecreatedb | usesuper | userepl | usebypassrls |  passwd  | valuntil | useconfig 
---------+----------+-------------+----------+---------+--------------+----------+----------+-----------
 notxor  |       10 | t           | t        | t       | t            | ******** |          | 
(1 row)

test=# select * from pg_database;
  oid  |  datname  | datdba | encoding | datcollate  |  datctype   | datistemplate | datallowconn | datconnlimit | datlastsysoid | datfrozenxid | datminmxid | dattablespace |            datacl             
-------+-----------+--------+----------+-------------+-------------+---------------+--------------+--------------+---------------+--------------+------------+---------------+-------------------------------
 13299 | postgres  |     10 |        6 | es_ES.UTF-8 | es_ES.UTF-8 | f             | t            |           -1 |         13298 |          478 |          1 |          1663 | 
 16384 | test      |     10 |        6 | es_ES.UTF-8 | es_ES.UTF-8 | f             | t            |           -1 |         13298 |          478 |          1 |          1663 | 
     1 | template1 |     10 |        6 | es_ES.UTF-8 | es_ES.UTF-8 | t             | t            |           -1 |         13298 |          478 |          1 |          1663 | {=c/notxor,notxor=CTc/notxor}
 13298 | template0 |     10 |        6 | es_ES.UTF-8 | es_ES.UTF-8 | t             | f            |           -1 |         13298 |          478 |          1 |          1663 | {=c/notxor,notxor=CTc/notxor}
(4 rows)

test=# \q

Bien, parece que todo ha funcionado correctamente. Es el momento de pararlo a la espera de necesitarlo más adelante: base-de-datos stop. Pero ya lo tengo listo para cuando haga falta.

¿Por qué no lo hago por el método tradicional de activar un demonio? Sería el método más normal, si quisiera tener un servidor de datos permanente, sin embargo, sólo necesitaré acceder a PostgreSQL cuando esté haciendo las pruebas.

Preparando phoenix

Como he dicho antes phoenix es un framework destinado a realizar aplicaciones web muy influido por ruby on rails, pero desarrollado en elixir. Puesto que el proyecto que tengo pensado pasa por desarrollar, precisamente, una página web con servicios de un servidor para múltiples clientes, creo que dicho framework es una buena herramienta para probar.

Las dependencias principales ya las tengo satisfechas: elixir y PostgreSQL. También depende de erlang, porque elixir necesita una máquina virtual donde correr y para ello utiliza BEAM, pero bueno, no la he nombrado porque se puede dar por obvia al instalarme el lenguaje.

Para instalar el framework hay que tirar del gestor de paquetes de elixir. No llego a entender la tendencia de todos los lenguajes a tener su propio gestor de paquetes. Bueno, sí la entiendo pero me sigue pareciendo una forma de ensuciar una instalación del sistema operativo y una manera de generar conflictos y duplicar librerías. Pero es cierto que al final, pip para Python, gem para Ruby, npm para node.js, cargo para Rust, rebar3 para erlang o mix para elixir, por poner unos ejemplos, son herramientas que han llegado para quedarse. De nada sirve quejarse, los fans de cada lenguaje te hablan maravillas de la suya en cuestión. Aunque en esencia son todas el mismo perro. Pero vamos con la instalación:

mix archive.install hex phx_new

Aparecen una serie de mensajes, pide confirmación y, en caso afirmativo, lo instala en el directorio local de paquetes:

Resolving Hex dependencies...
Dependency resolution completed:
New:
  phx_new 1.5.7
* Getting phx_new (Hex package)
All dependencies are up to date
Compiling 10 files (.ex)
Generated phx_new app
Generated archive "phx_new-1.5.7.ez" with MIX_ENV=prod
Are you sure you want to install "phx_new-1.5.7.ez"? [Yn] y
* creating /home/notxor/.mix/archives/phx_new-1.5.7

Ya está instalado, voy a hacer una pequeña prueba a ver si todo funciona como debe, sin meterme mucho en profundidades, ─que aún no tengo yo tampoco muy claro cómo funciona el chismático─.

Inicio o creación de un nuevo proyecto:

mix phx.new hola_phoenix

Ese comando genera todo el esqueleto de la aplicación. Pero aún no la base de datos, que tenemos que crear nosotros a mano.

* creating hola_phoenix/config/config.exs
* creating hola_phoenix/config/dev.exs
* creating hola_phoenix/config/prod.exs
* creating hola_phoenix/config/prod.secret.exs
* creating hola_phoenix/config/test.exs
* creating hola_phoenix/lib/hola_phoenix/application.ex
* creating hola_phoenix/lib/hola_phoenix.ex
* creating hola_phoenix/lib/hola_phoenix_web/channels/user_socket.ex
* creating hola_phoenix/lib/hola_phoenix_web/views/error_helpers.ex
* creating hola_phoenix/lib/hola_phoenix_web/views/error_view.ex
* creating hola_phoenix/lib/hola_phoenix_web/endpoint.ex
* creating hola_phoenix/lib/hola_phoenix_web/router.ex
* creating hola_phoenix/lib/hola_phoenix_web/telemetry.ex
* creating hola_phoenix/lib/hola_phoenix_web.ex
* creating hola_phoenix/mix.exs
* creating hola_phoenix/README.md
* creating hola_phoenix/.formatter.exs
* creating hola_phoenix/.gitignore
* creating hola_phoenix/test/support/channel_case.ex
* creating hola_phoenix/test/support/conn_case.ex
* creating hola_phoenix/test/test_helper.exs
* creating hola_phoenix/test/hola_phoenix_web/views/error_view_test.exs
* creating hola_phoenix/lib/hola_phoenix/repo.ex
* creating hola_phoenix/priv/repo/migrations/.formatter.exs
* creating hola_phoenix/priv/repo/seeds.exs
* creating hola_phoenix/test/support/data_case.ex
* creating hola_phoenix/lib/hola_phoenix_web/controllers/page_controller.ex
* creating hola_phoenix/lib/hola_phoenix_web/templates/layout/app.html.eex
* creating hola_phoenix/lib/hola_phoenix_web/templates/page/index.html.eex
* creating hola_phoenix/lib/hola_phoenix_web/views/layout_view.ex
* creating hola_phoenix/lib/hola_phoenix_web/views/page_view.ex
* creating hola_phoenix/test/hola_phoenix_web/controllers/page_controller_test.exs
* creating hola_phoenix/test/hola_phoenix_web/views/layout_view_test.exs
* creating hola_phoenix/test/hola_phoenix_web/views/page_view_test.exs
* creating hola_phoenix/lib/hola_phoenix_web/gettext.ex
* creating hola_phoenix/priv/gettext/en/LC_MESSAGES/errors.po
* creating hola_phoenix/priv/gettext/errors.pot
* creating hola_phoenix/assets/webpack.config.js
* creating hola_phoenix/assets/.babelrc
* creating hola_phoenix/assets/js/app.js
* creating hola_phoenix/assets/css/app.scss
* creating hola_phoenix/assets/js/socket.js
* creating hola_phoenix/assets/package.json
* creating hola_phoenix/assets/static/favicon.ico
* creating hola_phoenix/assets/css/phoenix.css
* creating hola_phoenix/assets/static/images/phoenix.png
* creating hola_phoenix/assets/static/robots.txt

Fetch and install dependencies? [Yn] 
* running mix deps.get
* running mix deps.compile
* running cd assets && npm install && node node_modules/webpack/bin/webpack.js --mode development

We are almost there! The following steps are missing:

    $ cd hola_phoenix

Then configure your database in config/dev.exs and run:

    $ mix ecto.create

Start your Phoenix app with:

    $ mix phx.server

You can also run your app inside IEx (Interactive Elixir) as:

    $ iex -S mix phx.server

Al terminar el proceso vemos que nos sugiere ya las cosas que debemos hacer a continuación. Lo primero ir al directorio base del proyecto. En nuestro caso hola_phoenix. A continuación tenemos que modificar el archivo config/dev.exs. Por eso la base de datos tenemos que generarla a mano, porque seguramente la conexión al servidor fallará con los datos por defecto. Al abrir el fichero vemos algo como:

# Configure your database
config :hola_phoenix, HolaPhoenix.Repo,
  username: "postgresql",
  password: "postgresql",
  database: "hola_phoenix_dev",
  hostname: "localhost",
  show_sensitive_data_on_connection_error: true,
  pool_size: 10

Tenemos que cambiar el valor de username y password para ajustarla a lo que solicite nuestro servidor. Además en ese mismo archivo podemos configurar los datos del servidor, como el puerto, por ejemplo, e incluso nos avisa de que si queremos utilizar HTTPS con las pruebas, podemos generar un certificado autofirmado con el comando:

mix phx.gen.cert

Una vez configurados los datos de acceso a la base de datos, tenemos que crearla:

   ≻ mix ecto.create
Compiling 14 files (.ex)
Generated hola_phoenix app
The database for HolaPhoenix.Repo has already been created

La herramienta ecto se encarga de todo lo referente al mantenimiento y comunicación con la base de datos.

Lo siguiente es probar cómo funciona levantando el servidor con el comando mix phx.server. El resultado lo veremos si dirigimos nuestro navegador favorito a http://localhost:4000:

Captura-pantalla_phoenix-funcionando.png

La otra opción es ejecutar nuestro servidor dentro de la aplicación interactiva de elixir, que se llama iex. El comando, como nos sugiere el aviso tras crear el proyecto es:

iex -S mix phx.server

¿Para qué podemos querer ejecutar la aplicación dentro de un prompt interactivo? Pues por ejemplo, para tener acceso a la herramienta observer que cargará con la máquina virtual de erlang. Con ella podemos observar la estructura de nuestra aplicación:

Captura-pantalla_observer.png

o mirar los recursos que está utilizando nuestro sistema:

Captura-pantalla_observer-grafico.png

Para acceder a esa herramienta, basta con teclear en el prompt interactivo la instrucción:

iex(1)> :observer.start()

o también, con la instrucción :debugger.start(), acceder al debugger:

Captura-pantalla_debugger.png

Conclusiones

De momento, tampoco controlo mucho de cómo funciona todo esto: estoy en fase de aprendizaje. Pero hay cosas que me suenan a erlang. Por ejemplo, en erlang también se podía acceder a las herramientas con observer:start(). o debugger:start().

Ya iré poniendo por aquí algunos avances según vaya aprendiendo más cosas. Si alguien tiene alguna sugerencia, se agradecerá el comentario.

Categoría: elixir erlang

Comentarios

Debido a algunos ataques mailintencionados a través de la herramienta de comentarios, he decidido no proporcionar dicha opción en el Blog. Si alguien quiere comentar algo, me puede encontrar en esta cuenta de Mastodon, también en esta otra cuenta de Mastodon y en Diaspora con el nick de Notxor.

Si usas habitualmente XMPP (si no, te recomiendo que lo hagas), puedes encontrar también un pequeño grupo en el siguiente enlace: notxor-tiene-un-blog@salas.suchat.org

Disculpen las molestias.