Aprender elixir
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:
elixir
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
:
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:
o mirar los recursos que está utilizando nuestro sistema:
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
:
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.
Comentarios