Lisp para desarrollo web

1 Introducción

En este artículo voy a explicar cómo hacer desarrollo Web con Lisp. Aunque no sería el orden lógico no voy a explicar aquí cual es la ventaja de usar Lisp para desarrollo Web ya que eso me llevaría un artículo completo en el que explicaría en general por qué Lisp es tan genial. Como digo, eso será parte de otro futuro artículo pero de momento os dejo esto para curiosos y gente ya convencida de lo genial que es usar Lisp y si no lo ha hecho para Web pues explico un poco qué opciones tenemos los Lisperos.

Como tiende a ser habitual en mis artículos lo haré en varias partes para que no sea demasiado aburrido. En esta primera parte hablaré de algunas opciones para el desarrollo Web en Lisp y un rápido tutorial de cómo empezar, haciendo una pequeña página de ejemplo. Posteriormente en una segunda o tercera parte nos meteremos en realizar una aplicación web mucho más compleja con gestión de usuarios, bases de datos, etc.

Así que para curiosos y amantes de Lisp, aquí empezamos el desarrollo Web en Lisp.

2 Preparando el entorno de desarrollo

Primero vamos a instalar un Instalar un compilador o implementación de Lisp. Podemos usar varios pero en este caso usaremos SBCL, porque es el que suelo usar y además ya tengo Slime configurado para que lo use. Hay otros como Clisp que también podríamos usar.

En fin, instalamos pues SBCL.

pacman -S sbcl

Ahora vamos a descargar un programa que sirve para gestionar las librerías que necesitemos. Para ello vamos a usar Quicklisp que puede cargar como unas 1500 librerías. Quicklisp además usa un sistema llamado ASDF que ya veremos para saber qué librerías necesita cargar nuestro proyecto y posee su propio REPL. No es igual que Gulp o Grunt, para eso en Lisp tenemos Roswell que funciona sobre Quicklisp pero su manejo es sobre consola.
Descargar Quicklisp.

Bien para instalar Quicklisp vamos a hacer lo siguiente en consola:

curl -O https://beta.quicklisp.org/quicklisp.lisp

Ejecutar sbcl cargando quicklisp

sbcl --load quicklisp.lisp

Una vez hecho esto instalamos quicklisp siguiendo las instrucciones:

(quicklisp-quickstart:install)

Si queremos que siempre que ejecutemos sbcl ya nos cargue quicklisp escribiremos lo siguiente:

(ql:add-to-init-file)                                                     

Para mejorar el trabajo dentro de quicklisp y tener algunas mejoras como el hecho de repetir comandos ya escritos pulsando las flechas hacia arriba y hacia abajo es interesante instalar el paquete rlwrap. Así lo que haremos es:

pacman -S rlwrap

Y luego ejecutaremos sbcl de la siguiente manera:

rlwrap sbcl

También podemos crear algún alias para que nos sea más cómodo.

3 Opciones a elegir

Tenemos como dos muy buenas opciones aunque no son las únicas:

De momento vamos a ver lo que nos ofrece Radiance

4 Radiance

Radiance es una aplicación de desarrollo para web, es parecido a un framework pero más general y más flexible. Nos permite, por ejemplo, crear páginas personales y generar aplicaciones fácilmente.

4.1 Obteniendo Radiance

Por diversas circunstancias, Radiance no está provista a través de la distribución estándar de Quicklisp. Sin embargo, Quicklisp soporta múltiples distribuciones (llamadas «dists») y añadir una nueva es solo añadir una cosa en una única línea. Con lo siguiente añadimos la correspondiente distribución e instalamos Radiance.

(ql-dist:install-dist "http://dist.tymoon.eu/shirakumo.txt")
(ql:quickload :radiance)

Para ejecutar Radiance y poder ver que todo funciona correctamente ejecutaremos el siguiente código

(radiance:startup)

La primera vez tardará algo ya que tiene que instalar muchos elementos aquí llamados interfaces.

Una vez hecho esto nos dirá que en nuestro navegador favorito podemos abrir el siguiente enlace:

http://localhost:8080
radiance
Radiance en su primer inicio

Para parar este servicio escribiremos:

(radiance:shutdown)

4.2 Creando una página simple de ejemplo

Vamos una simple aplicación web como ejemplo. En el argot de Radiance a cada aplicación web, sea lo que sea se le llama módulo. Así que para ello vamos a crear una pequeña estructura con los ficheros básicos. Para ello solo tenemos que escribir en Quicklisp los siguiente:

(radiance:create-module "mi-modulo")

Podemos sustituir la palabra mi-modulo por el nombre que le queramos poner a nuestro módulo.

Esto nos creará un directorio dentro del directorio de local-projects de la carpeta Quicklisp.

Luego veremos cómo crear nuestra carpeta de nuestro módulo en cualquier parte pero de momento la dejaremos ahí.

Nuestro directorio, llamado como nuestro módulo tendrá la siguiente estructura.

.
├── mi-modulo.asd
├── mi-modulo.lisp
├── static
└── template

Así vemos cómo se crean dos directorios llamados static y template donde vamos a poner en ellos plantillas y ficheros estáticos como hojas de estilo, fichero de javascript, etc.

Veamos ahora los dos ficheros creados. Primero el fichero con extensión asd que es un fichero que usa el sistema ASDF. Resumiendo mucho los ficheros ASD son como los ficheros Makefile en C, son ficheros donde definimos lo que aquí llaman sistema que incluye las dependencias y los ficheros que usa. Gracias a quicklisp y ese fichero cargaremos y compilaremos lo necesario.

Así veamos el fichero mi-modulo.asd

(in-package #:cl-user)
(asdf:defsystem #:mi-modulo
  :defsystem-depends-on (:radiance)
  :class "radiance:virtual-module"
  :components ((:file "mi-modulo"))
  :depends-on NIL)

Resumidamente vemos como requiere el paquete cl-user definimos el sistema con la dependencia de radiance y le decimos que el fichero que necesita es el fichero mi-modulo sin necesidad de poner la extensión.
Para más información, tenemos la página de ASDF.

Ahora para cargar nuestro módulo tendremos que escribir en SBCL:

(ql:quickload :mi-modulo)

Si tenemos Radiance activado, con la orden (radiance:startup) nos dará un error, sino el error lo dará cuando activemos Radiance y el error será que no tenemos versión de nuestro módulo y no es posible la migración. Esto significa que Radiance tiene un sistema automático que ayuda a la migración cuando actualizamos nuestro módulo. Cuando nos dé este error sólo tendremos que pulsar 0. El error en concreto será este:

0: [CONTINUE] Don't migrate the system and continue.
1: [ABORT   ] Exit debugger, returning to top level.

Si no queremos que este error nos siga molestando tendremos que poner en el fichero asd el número de la versión quedando éste así:

(in-package #:cl-user)
(asdf:defsystem #:mi-modulo
  :version "0.0.1"
  :defsystem-depends-on (:radiance)
  :class "radiance:virtual-module"
  :components ((:file "mi-modulo"))
  :depends-on NIL)

Una vez que hayamos cargado nuestro módulo vamos a la siguiente página http://localhost:8080/mi-modulo y veremos como aparece lo mismo que en http://localhost:8080 y eso es porque aún no hemos escrito nada. Pero si en el fichero mi-modulo.lisp definimos nuestra primera página con el siguiente código, quedará el fichero de la siguiente forma:

(in-package #:rad-user)

;; definimos la página
(define-page mi-modulo "/mi-modulo" ()
  (setf (content-type *response*) "text/plain")
  "Hola Mundo")

(define-module #:mi-modulo
  (:use #:cl #:radiance))
(in-package #:mi-modulo)

Ahora volvemos a cargar el módulo con y recargamos la página web anterior.

(ql:quickload :mi-modulo)
Radiance
Nuestro primer hola mundo en Radiance

4.3 Separando las cosas para mejor comprensión

Vemos que tenemos en un mismo fichero la definición del módulo y la de la página a la vez. Cuando solo ponemos hola mundo no pasa nada pero imaginaos que vamos a escribir una o varias páginas con mucho código. Sería un verdadero engorro. Así que la solución más lógica sería sacar ese código y dejar un solo fichero para el módulo y otro u otros para el código de la página. Y eso es lo que vamos a hacer. Para ello vamos a renombrar nuestro fichero mi-modulo.lisp símplemente a modulo.lisp y vamos a crear un fichero llamado frontend.lisp donde vamos a definir las páginas que usemos para el módulo. Una vez hecho esto llevaremos el código que define la página ahí y lo registraremos en el fichero asdf para que lo cargue y compile bien. Así nuestros ficheros se van a quedar de la siguiente manera:

Fichero mi-modulo, que pasa a ser modulo.lisp

(in-package #:rad-user)
(define-module #:mi-modulo
  (:use #:cl #:radiance))
(in-package #:mi-modulo)

Fichero mi-modulo.asd

(in-package #:cl-user)
(asdf:defsystem #:mi-modulo
  :version "0.0.1"
  :defsystem-depends-on (:radiance)
  :class "radiance:virtual-module"
  :components ((:file "modulo")
               (:file "frontend"))
  :depends-on NIL)

Como se ve, he añadido los ficheros que vamos a usar, modulo y frontend.

Fichero frontend.lisp

(in-package #:mi-modulo)
(define-page mi-modulo "/mi-modulo" ()
(setf(content-type *response*) "text/plain")
"Hola, de nuevo, Mundo!")

Como se ve he puesto el código referente a la definición de la página que ya teníamos en el fichero anterior y además he añadido la línea (in-package #:mi-modulo) que es la forma de conectar la definición de módulo con el fichero que define la página.

Además, he cambiado un poco el texto que mostrará para que así podamos observar el cambio.

Ahora para ver el cambio hay algunos problemas por la caché de Radiance y es que no veremos ningún cambio por mucho que recompilemos nuestro módulo. Así que lo conveniente es salir de SBCL con (quit) y hacer todo el proceso. Los comandos a seguir en SBCL son:

(ql:quickload :radiance)
(ql:quickload :mi-modulo)
(radiance:startup)

Y entonces veremos los cambios.

Pero, ¿qué pasa si trabajamos con varias páginas y además cada una de ellas tiene un código largo? Ponerlas todas en el mismo fichero sigue siendo una locura y la solución lógica sigue siendo la misma. Por eso vamos a trabajar con unas plantillas que va a ser donde vamos a poner el código que se muestre en las páginas y desde el fichero frontend.lisp las cargaremos.

Para las plantillas vamos a usar un sistema de plantillas llamado Clip y para acceder a esas plantillas usaremos una extensión llamada r-clip. En Clip básicamente escribiremos en html pero con algunas etiquetas especiales. Más adelante haremos un tutorial más completo acerca de estas plantillas. Si estás en Emacs estás plantillas son compatibles con el web-mode gracias a Bois Francois-Xavier.

Además usaremos Lass como gestor de estilos. Básicamente Lass es una versión de Sass, de ahí su nombre, usando un lenguaje más parecido a Lisp. También en un futuro realizaremos un tutorial más en profundidad. También podríamos usar Sass o Css directamente ya que a lo que vamos a enlazar en nuestra plantilla es al fichero Css generado, el cuál estará en la carpeta static.

Para compilar el fichero Lass escribiremos los siguiete en SBCL:

(ql:quickload :lass)
(lass:generate (asdf:system-relative-pathname :plaster "static/plaster.lass"))

Si usas Emacs puedes configurarlo para que teniendo ejecutado Slime lo compile directamente cuando se guarda. Para ello tienes que descargarte un fichero llamado lass.el que está aquí, guardarlo y escribir lo siguiente en tu fichero de configuración de Emacs:

(add-to-list 'load-path "[dirección de tu fichero]/")
(require 'lass)

Así, por un lado vamos a crear una plantilla muy simple para ver como funciona que alojaremos en la carpeta template. La llamaremos como queramos, mi-modulo, por ejemplo y su extensión será ctml.

La plantilla tendrá el siguiente código:

<!- código de la pantilla mi-modulo.ctml ->
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta charset="UTF-8" />
    <title>Página de Prueba</title>
    <link rel="stylesheet" type="text/css" href="../static/mi-modulo.css" @href="/static/mi-modulo/mi-modulo.css" />
  </head>
  <body>
    <header>
      <h1>Una página muy simple</h1>
    </header>
    <main>
      <p>Pero simple simple</p>
    </main>
  </body>
</html>

Como vemos aquí lo único novedoso es la etiqueta @href que es parte del sistema de enrutado que explicaremos más adelante.

El fichero lass estará en la carpeta de static y tendrá el siguiente código:

(body
 :font-family sans-serif
 :font-size 12pt
 :background "#eee")

(header
 :text-align center)

(main
 :width 800px
 :margin 0 auto 0 auto
 :background "#fff"
 :padding 10px
 :border 1px solid "#bbb"
 :border-radius 5px)

Ahora falta modificar el fichero frontend.lisp para que cargue nuestra plantilla. Para eso escribiremos lo siguiente y se quedará así:

(in-package #:mi-modulo)
(define-page ejemplo "/mi-modulo" (:clip "mi-modulo.ctml")
  (r-clip:process T))

Y no tenemos que olvidar decirle a nuestro sistema definido en el fichero ASDF que cargue r-clip para que procese y cargue las plantillas. Para eso finalmente el fichero mi-modulo.asd quedará así:

(in-package #:cl-user)
(asdf:defsystem #:mi-modulo
  :version "0.0.1"
  :defsystem-depends-on (:radiance)
  :class "radiance:virtual-module"
  :components ((:file "modulo")
               (:file "frontend"))
  :depends-on (:r-clip))
Hola mundo en Radiance
Una segunda versión del hola mundo más avanzada

4.4 Terminado esta primera parte

Una vez que volvamos a compilar nuestro módulo con (ql:quickload :mi-modulo) recargamos la página (http://localhost:8080/mi-modulo) y podremos tener nuestra primera web hecha con Lisp y Radiance. Si nos da algún problema por culpa de la caché rehacer el proceso desde el principio como ya sabemos.

5 Para las siguientes partes

Radiance tiene un montón de lo que ellos llaman «interfaces» que son como módulos para añadir características a su «frontend». Esas interfaces son:

  1. admin. Que proporciona una página para administración.
  2. auth. Que maneja todo lo de la autenticación y entrada de usuarios.
  3. ban. Que maneja el baneo de usuarios del sitio por su direccion IP.
  4. cache. Que proporciona un sistema simple de caché.
  5. database. Es una interfaz flexible de manejo tanto de bases de datos relacionales como almacenamiento basado en objetos.
  6. logger. Es una interfaz para logeo que muestra mensajes informativos.
  7. mail. Una interfaz para enviar correos
  8. profile. Para manejar los perfiles de usuarios y distintos campos.
  9. rate. Que permite limitar el acceso a ciertos recursos.
  10. server. Un puente entre Radiance y el universo exterior.
  11. session. Asegura la permanencia de los usuarios en la sesión.
  12. user. Proporciona cuentas de usuario y permisos para éstos.

No vamos a verlos todos pero si algunos de los más importantes ya que veremos como hacer aplicaciones más complejas en Lisp con usuarios, acceso a bases de datos, formularios, etc.

Comparte esto:
Share
admin Escrito por:

Sé el primero en comentar

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *