Biblioteca Yesod

De Wikihaskell
Saltar a: navegación, buscar
Biblioteca de programación web
Desarrollo web en Haskell con la biblioteca Yesod
Lenguaje Haskell
Biblioteca Yesod
Autores Jose Luis Aparicio Rodríguez
Pedro Liberal Fernández
Gloria María Vázquez Moreno

Yesod es un framework web Haskell que promueve la seguridad de tipos y el desarrollo de aplicaciones Web REST. Está diseñado a partir de principios que permiten ejecución eficiente. Proporciona todas las herramientas que se necesitan para crear rápidamente aplicaciones eficientes y seguras para la Web.

Yesod intenta facilitar el desarrollo de aplicaciones web aprovechando los puntos fuertes de Haskell. Gracias al sistema fuerte de tipos que tiene este lenguaje y a la transparencia referencial, tenemos garantías de que los programas son correctos y de que no se producen efectos secundarios.

Contenido

Instalación

En primer lugar necesitamos instalarnos el compilador GHC 6.12 que podemos descargarlo desde http://www.haskell.org/ghc/


Después de esto, instalamos la plataforma Haskell

    $ wget http://hackage.haskell.org/platform/2009.2.0.1/haskell-platform-2009.2.0.1.tar.gz	 
    $ tar -xvvf haskell-platform-2009.2.0.1.tar.gz
    $ cd haskell-platform-2009.2.0.1/
    $ ./configure
    $ make
    $ sudo make install

Luego nos descargamos e instalamos el paquete Cabal:

    $ sudo apt-get install cabal-install

Instalamos la biblioteca Yesod:

    $ cabal install yesod-0.5.0

Introducción

Seguridad de Tipos

Yesod usa técnicas declarativas de alto nivel. Pueden especificarse los tipos de entrada exactos que se esperan,y a la inversa, se pueden especificar los tipos de salida al enviar datos y asegurarnos de que estos tipos son correctos.

Conciso

Yesod no es el primer proyecto que lucha por objetivos como éstos, sin embargo, tiene un arma poderosa: Quasi-quotation. La Quasi-quotation permite la integración de una sintaxis arbitraria dentro del código fuente, que luego se convertirán en Haskell. Esto nos otorga una serie de beneficios.

Al permanecer en el código fuente, todo se comprueba en tiempo de compilación, esto hace que pueda asegurarse que el formato es adecuado y que es consistente con el resto del código.

Rendimiento

El compilador principal de Haskell, GHC, tiene características sorprendentes. La elección de este compilador da por sí mismo a Yesod una ventaja de rendimiento frente a otras ofertas.

El uso de "quasi-quotation" permite que HTML, CSS y JavaScript puedan ser analizados en tiempo de compilación, Yesod evita el coste de la E/S en tiempo de ejecución optimizando las prestaciones del código. Al ofrecer abstracciones de alto nivel, se puede conseguir un alto nivel de compresión y almacenado en caché CSS y JavaScript.

Modularidad

Yesod ha dado lugar a la creación de más de una docena de paquetes, todos estos puedan emplearse en un contexto fuera de Yesod. Uno de los objetivos del proyecto es contribuir a la comunidad tanto como sea posible.

Por supuesto, estas bibliotecas han sido diseñadas para integrarse conjuntamente. Utilizar el framework Yesod debe dar una fuerte coherencia a lo largo de las diversas APIs.

Primer Ejemplo

Una página web simple en la que se escribe "Hola Mundo!"

Hola Mundo

   1.{-# LANGUAGE TypeFamilies, QuasiQuotes #-}
   2.import Yesod
   3.data HolaMundo= HolaMundo
   4.mkYesod "HolaMundo" [$parseRoutes|
   5./ HomeR GET
   6.|]
   7.instance Yesod HolaMundo where approot _ = ""
   8.getHomeR = defaultLayout [$hamlet|Hola Mundo!|]
   9.main = basicHandler 3000 HolaMundo

Guardando el código en holamundo.hs y ejecutándolo con

   runhaskell holamundo.hs

se creará un servidor web en el puerto 3000 y accediendo con el navegador a http://localhost:3000 se podrá acceder a la página.

Si miramos el código fuente de la página aparecerá esto:

   <!DOCTYPE html>
   <html><head><title></title></head><body>Hola Mundo!</body></html>

Enrutamiento

En la mayoría de los sistemas de páginas web como PHP se tienen muchos archivos y el servidor se encarga de redirigir al usuario a la página que sea. Con Yesod siempre se accede al mismo punto y desde dentro de la aplicación se mandan las peticiones al archivo que sea. Las lineas de la 4 a la 6 son las que se encargan de hacer esto. En esta aplicación el único recurso es el de la línea 5. Con la barra / se está indicando que se aceptan peticiones en la raíz de la aplicación, que son de tipo GET y que el nombre del recurso es HomeR.

Función asistente

Cuando Yesod ve el nombre del recurso en una petición, lo que hace es llamar a la función que se encarga de este recurso, el nombre de la función sería el método de petición del recurso en minúsculas en este caso "get" seguido del nombre del recurso, getHomeR. En getHomeR se llama a defaultLayout que mediante el uso de código html por defecto, crea la página html con "Hola Mundo!" en el cuerpo.

La base

Todas las aplicaciones en Yesod necesitan un tipo de datos que será la base de la aplicación, en este caso es HolaMundo, que se define en la linea 3. En la linea 4 se asocia la ruta de redireccionamiento con este tipo de datos. El tipo de datos base tiene que ser una instancia de la clase Yesod. Esto es lo que se hace en la línea 7.

En esta instancia se definen algunos parámetros de la configuración del programa, todos los parámetros menos approot tienen un valor por defecto. El parámetro approot lo que hace es decirle al programa cual es la raíz de la página web. Cuando la cadena está vacía lo que hace es que toma como raíz, la raíz del servidor web.

Ejecución

Luego definimos la función main usando la función basicHandler que crea un servidor en el puerto especificado, 3000 en este caso. BasicHandler comprueba si hay variables de entorno CGI presente. Si es así, se ejecuta como CGI. De lo contrario, como un servidor independiente.

Recursos y y direcciones URL con seguridad de tipos

1. {-# LANGUAGE TypeFamilies, QuasiQuotes #-}
2. import Yesod
3. data Enlaces = Enlaces
4. mkYesod "Enlaces" [ $ parseRoutes |
5. / HomeR GET
6. / page1 Page1R GET
7. / page2 Page2R GET
8. | ]
9. instance Yesod Enlaces where approot _ = ""
10. getHomeR = defaultLayout [ $ hamlet |% a ! href =@ Page1R @ Go to page 1 !| ]
11. getPage1R = defaultLayout [ $ hamlet |% a ! href =@ Page2R @ Go to page 2 !| ]
12. getPage2R = defaultLayout [ $ hamlet |% a ! href =@ HomeR @ Go home !| ]
13. main = basicHandler 3000 Enlaces 

En la línea 10, % A! href = @ @ Page1R Go to page 1 crea un vínculo al recurso Page1R. Page1R es un constructor de datos. Al ser cada recurso un constructor de datos, tenemos una función llamada de seguridad URL tipo.

Plantillas

Yesod ofrece 3 sistemas de plantillas: Hamlet (HTML), Cassius (CSS) and Julius (Javascript). Estos sistemas están disponibles para su uso fuera del ámbito de Yesod.

Hamlet

Cualquier sistema de plantillas se pueden utilizar con Yesod, como Yesod se construye en la parte superior de la WAI , sólo tenemos que ser capaces de producir un cuerpo de la respuesta comprensible para esa interfaz. Sin embargo, Yesod dio lugar a la creación de un sistema de plantillas Hamlet que está diseñado para interactuar bien con el ecosistema Yesod. También es una herramienta muy útil fuera de Yesod, y es posiblemente el más popular de los proyectos que se escindió de Yesod.

Tipo de seguridad

Una de las mayores características de Yesod es su seguridad de tipo penetrante. Este fue el impulso inicial para la creación de Hamlet. Como tal, todos los interpolación de variables se comprueba en tiempo de compilación. Hamlet admite tres formas de interpolación:

• Interpolación signo de dólar: Mediante cualquier instancia de typeclass ToHTML. Esto incluye la cadena y en HTML.

• Interpolación arroba: (URL de tipo seguro) Hamlet permite interpolar las direcciones URL con seguridad de tipos directamente.

• Interpolación de símbolo de intercalación: Mediante otras pantillas Hamlet. Es excelente para reutilizar el código en otras plantillas.

Cassius (CSS) ofrece soporte para las dos primeras formas de interpolación, pero en lugar de usar typeclass ToHTML, se usa typeclass ToCss.

Jullius (Javascript) proporciona soporte para los tres, sin embargo, en lugar de interpolación signo de dólar, Jullius utiliza el signo de porcentaje. Esto es para que sea más natural para hacer frente a la librería jQuery Javascript, que tiene un significado especial para el signo de dólar.

Diseño por defecto

Editar una cabecera HTML ,el título, y las etiquetas de cuerpo, así como el tipo de documento para cada página sería tedioso, además esto conlleva una gran cantidad de repeticiones de código y hace que sea difícil tener una distribución uniforme de las páginas. Vamos a ver cómo utilizar defaultLayout o diseño por defecto en Yesod.

1. getHomeR = defaultLayout $ do
2.     setTitle "Home Page"
3.     addHamlet [$hamlet|%h1 Hola Mundo|]

El código HTML resultante sería el siguiente:

1. El código HTML resultante sería el siguiente:
2. <!DOCTYPE html>
3. <html>
4.     <head>
5.         <title>Home Page</title>
6.     </head>
7.     <body>
8.         <h1>Hola Mundo</h1>
9.     </body>
10. </html>

Cassius

También podemos añadir declaraciones CSS junto con el diseño por defecto.

1. getHomeR = defaultLayout $ do
2.     setTitle "Home Page"
3.     addHamlet [$hamlet|%h1 Hello World|]
4.     addCassius [$cassius|
5.     h1
6.         color: green
7.     |]

Cassius, como Hamlet, utiliza un formato sensible a los espacios en blanco. También permite la interpolación de variables como Hamlet, que le permite evitar errores ortográficos en los nombres de clase.

Julius

Yesod además de trabajar con css, también trabaja con javascript, para ello se utiliza Julius.

Julius no nos proporciona ninguna sintaxis de ayuda, sólo permite hacer interpolaciones de variables, aquí tenemos un ejemplo:

1. getNamesR name = defaultLayout $ do
2.     addJulius [$julius|alert("Hello %name%");|]

Hay que tener en cuenta que usamos signos de porcentaje en vez de signos de dólar para Julius.

Plantillas externas

Quasi-quoting en las plantillas puede ser conveniente, ya que no son necesarios archivos adicionales , la plantilla está cercana al código, y no se hace una recompilación automáticamente cada vez que haya cambios en la plantilla.

Hamle ofrece dos conjuntos de funciones para incluir una plantilla externa:

  • hamletFile/cassiusFile/juliusFile
  • hamletFileDebug/cassiusFileDebug/juliusFileDebug

¿Por qué tenemos dos tipos de funciones?

El primero incorpora plenamente el contenido de la plantilla en el código en tiempo de compilación y nunca se ve en la plantilla de nuevo hasta que vuelva a compilar. Esto es ideal para un entorno de producción: compilar el código y no tener que depender del tiempo de ejecución en ninguna plantilla. También evita una penalización de tiempo de ejecución al leer un fichero.

El conjunto de funciones de depuración se destina para desarrollo. Estas funciones trabajan con un poco de la magia: en tiempo de compilación, inspeccionan la plantilla, determinan a qué variables hacen referencia, y generan un código Haskell para cargar las variables. En tiempo de ejecución, leen la plantilla de nuevo y se alimentan en esas variables.

Exceptuando las plantillas cortas , Así es probablemente cómo se van a escribir la mayoría de las plantillas en Yesod. La estructura de archivos típica es la creación de "Hamlet", Cassius y carpetas "Julius" y colocar las respectivas plantillas de cada uno. Cada plantilla tiene una extensión que coincide con el lenguaje de la plantilla.

En otras palabras, típicamente tendría:

 1. # hamlet/homepage.hamlet
 2. %h1 Hello World!

 3. # cassius/homepage.cassius
 4. h1
 5.     color: green

 6. # julius/homepage.julius
 7. alert("Mensaje de alerta");
 
 8. # Settings.hs, paraphrasing

 9. import qualified Text.Hamlet
10. import qualified Text.Cassius
11. import qualified Text.Julius

12. hamletFile x = Text.Hamlet.hamletFileDebug $ "hamlet/" ++ x ++ ".hamlet"
13. -- igual para cassius y julius

14. # Finalmene escribimos el código de prueba
15. import Settings
16. getHomeR = defaultLayout $ do
17.     setTitle "Homepage"
18.     addHamlet $(hamletFile "homepage")
19.     addCassius $(cassiusFile "homepage")
20.     addJulius $(juliusFile "homepage"

Widgets

Una de las dificultades de desarrollo web es que en el lado del cliente hay que coordinar tres tecnologías diferentes: HTML, CSS y Javascript. El código CSS dentro de la etiqueta <style>, el código Javascript dentro de <script> y el código HTML dentro del cuerpo, <body>. Si se quiere poner el código CSS y Javascript en archivos separados se complica aún más.

Suele ser fácil cuando nada más que se tiene una página web, porque se pueden separar el código HTML (estructura), CSS (estilo) y lógica (Javascript) sin problemas. Sin embargo, cuando se quiere reusar secciones de código porque se tiene más de un archivo y se desea hacer modular, entonces es más díficil coordinar las tres tecnologías. Para solucionar esto se utilizan widgets.

Ejemplo de Hola Mundo con Widgets

   1.{-# LANGUAGE TypeFamilies, QuasiQuotes #-}
   2.import Yesod
   3.data HolaMundo= HolaMundo
   4.mkYesod "HolaMundo" [$parseRoutes|
   5./ HomeR GET
   6.|]
   7.instance Yesod HolaMundo where approot _ = ""
   8.getHomeR = defaultLayout $ do
   9.       setTitle "Hola Mundo"
   10.      [$hamlet|%h1!style="color:red" Hello World!|]
   11.main = basicHandler 3000 HolaMundo

Generará el siguiente código fuente:

     <!DOCTYPE html> 
     <html><head><title>Hola Mundo</title></head><body><h1 style="color:red">Hola Mundo!</h1></body></html> 
Ejemplo Widgets.png

La función defaultLayout toma como argument un Widget. Un Widget es una mónada que contiene información sobre el título de la página, el tag head, estilos, código javascript y el cuerpo que forman el documento. defaultLayout organiza todo esto en componentes y los coloca en la plantilla de HTML.

Cuando ponemos una plantilla hamlet en un widget, el contenido se añade al cuerpo de la página, es decir el contenido entre las etiquetas <body> y </body>. Hay otras funciones auxiliares como setTitle que ponen contenido en otra parte de la página, esta en concreto lo pone entre las etiquetas <title></title>. Si queremos que el texto sea rojo por ejemplo, una de las maneras de hacerlo es con el atributo style dentro del código hamlet.








Formularios

Aplicación suma/resta

   1.data Params = Params
   2.	{ operando1 :: Int
   3.	, operando2 :: Int
   4.	, operacion :: String
   5.	}
   6.
   7.paramsFormlet :: Maybe Params -> Form s m Params
   8.
   9.paramsFormlet mparams = fieldsToTable $ Params
   10.	<$> intField "Operando 1 (int):" (fmap operando1 mparams)
   11.	<*> intField "Operando 2 (int):" (fmap operando2 mparams)
   12.	<*> stringField "Operacion (+, -):" (fmap operacion mparams)
   13.
   14.getRootR :: Handler RepHtml
   15.getRootR = do
   16.	(res, form, enctype) <- runFormGet $ paramsFormlet Nothing
   17.	output <-
   18.		 case res of
   19.		     FormMissing -> return "Rellene el formulario."
   20.		     FormFailure _ -> return "Operacion no valida."
   21.		     FormSuccess (Params op1 op2 op) -> do
   22.			let resultado = if op == "+" then (op1 + op2) else (op1-op2)
   23.			return $ "El resultado es " ++ show resultado
   24.	defaultLayout [$hamlet|
   25.
   26.%p $output$
   27.%form!enctype=$enctype$
   28.	%table
   29.		 ^form^
   30.		 %tr
   31.		    %td!colspan=2
   32.			     %input!type=submit!value="Calcular"
   33.|]

Este ejemplo muestra el uso de los formularios. Con Yesod los parametros de los formularios se pueden validar automáticamente sin que tengamos que hacer comprobaciones en el código. Para esto sólo tenemos que darle un tipo de datos concreto a cada parámetro del formulario y Yesod se encargará de comprobar de que son correctos. Por ejemplo, si donde esperamos un entero, hay una cadena, Yesod detectará que hay errores en el formulario. Lo primero que hay que hacer es crear un tipo de datos que contendrá toda la información que necesitaremos para esta aplicación de suma y resta. En este caso en concreto necesitamos el primero operando (int), el segundo operando (int) y un carácter que representará la operación deseada (suma o resta). Al tipo lo hemos llamado Params.

Las líneas de la 7 a la 12 introducen la API de formularios de Yesod. La función paramsFormlet toma como entrada un dato del tipo Maybe Params, esto significa que puede que se le pasen datos a la función, los cuales se usarán como valores por defecto al formulario o puede que no se le pase parámetros del formulario (Nothing). Esta función devuelve un dato del tipo Form que es un formulario.

En la línea 10 vemos que aparece la palabra intField, esto especifica que queremos un valor de tipo entero, luego "Operando 1 (int)" es la etiqueta que le aparece al usuario al lado del campo de texo y la última parte de la linea proporciona el valor inicial que tendrá el campo al cargarse la página.


Cuando el usuario ha envíado el formulario con valores, el valor que se usa como valor inicial para el campo al cargarse de nuevo la página es el que envió el usuario. Si es la primera vez que se carga la página, no habrá valor inicial a no ser que proporcionemos uno explícitamente en el código al llamar a paramsFormlet. Las líneas 11 y 12 son iguales. En la línea 9, donde pone fieldsToTable se está indicando que se quieren mostrar los campos del formulario como una tabla en la que hay una fila por campo.

En la línea 16 llamamos a la función paramsFormlet con el parámetro Nothing, es decir el formulario empieza en blanco sin valores por defecto. Este valor se lo pasamos a runFormGet que sirve para enlazar el formulario con parámetros GET. Si los datos se quieren enviar a través de POST, se usa runFormPost. Esta función devuelve una tupla de 3 elementos, que contiene lo siguiente:

  • El resultado de validar el formulario, FormMissing cuando no se han enviado datos, FormFailure cuando al validar los datos ha habido error de tipos y FormSuccess cuando todo ha ido bien.
  • El segundo es elemento es el formulario en sí.
  • El valor del atributo enctype del formulario.

Referencias

http://docs.yesodweb.com/

http://blog.syntaxvssemantics.com/2010/10/installing-yesod-haskell-web-framework.html


Herramientas personales