Adrianistán

Tutorial de Maud, motor de plantillas HTML para Rust

31/01/2017
Seguimos aprendiendo en el blog sobre interesantes proyectos hechos para Rust. Ya hemos visto Iron, Piston y Neon. Hoy veremos Maud, un potente motor de plantillas que se centra en la eficiencia. Maud se compara a otras soluciones como Razor, ERB, Liquid,  Handlebars o Jade pero esta vez escribiremos nuestro HTML en Rust. ¿Locura? No, y de hecho funciona de forma bastante transparente. Vamos a verlo en acción

Comparativa de velocidad de diferentes motores. Maud es el más rápido (menos es mejor)

Instalando Maud


Maud usa plugins del compilador, una característica que a día de hoy no está activado ni en el canal estable ni el canal beta, solamente en el canal nightly. Para obtener una copia de Rust nightly lo ideal es usar Rustup.

Una vez hecho eso, creamos un nuevo proyecto y añadimos las dependencias de Maud al fichero Cargo.toml.


maud = "*"
maud_macros = "*"


Una simple plantilla


Ahora abrimos el archivo src/main.rs y vamos a empezar a usar Maud.


#![feature(plugin)]
#![plugin(maud_macros)]

extern crate maud;

fn main(){
let name = "Adrián";
let markup = html!{
p { "Hola, soy " (name) " y estoy usando Maud"}
};
println!("{}", markup.into_string());
}



La potencia de Maud se ve en la mega-macro html!. En esta macro escribiremos la plantilla que será compilada de forma nativa a Rust, lo que nos asegura una velocidad de ejecución excepcional. En este caso la salida será una etiqueta P de párrafo con la variable interpolada.

Simple, ¿no?

PreEscaped y otros elementos básicos


Por defecto en Maud todos el texto se convierte a HTML seguro. Es decir, no se pueden introducir etiquetas nuevas en el texto. Si por alguna razón necesitásemos añadir etiquetas nuevas podemos usar PreEscaped, que no realiza esta transformación de seguridad. Veamos el siguiente código:


#![feature(plugin)]
#![plugin(maud_macros)]

extern crate maud;

use maud::PreEscaped;

fn main(){
let name = "Adrián";
let markup = html!{
p { "Hola, soy " (name) " y estoy usando Maud" }
p { "<h5>Esto no funcionará</h5>" }
p { (PreEscaped("<h5>Esto sí funcionará</h5>")) }
};
println!("{}", markup.into_string());
}



El primer H5 se convertirá a código HTML seguro, es decir, no añadirá la etiqueta, en cambio se verá h5 en la web. Por contra con PreEscaped se añadirá la etiqueta h5 tal cual.

Los elementos son muy fáciles de añadir en Maud y por lo general no deberías usar PreEscaped salvo contadas ocasiones. Veamos como añadir más etiquetas.


#![feature(plugin)]
#![plugin(maud_macros)]

extern crate maud;

fn main(){
let name = "Adrián";
let markup = html!{
p { "Hola, soy " (name) " y estoy usando Maud" }
p {
"Si la montaña no viene a Mahoma"
br /
"Mahoma va la montaña"
small em "Atribuido a Francis Bacon"
}
};
println!("{}", markup.into_string());
}



En este ejemplo vemos como las etiquetas que no llevan texto como BR o INPUT debemos cerrarlas con una barra. Por otro lado es posible aglutinar varios niveles de etiquetas en una sola línea ( SMALL->EM->Texto).

Atributos, clases e IDs


En Maud es posible asignar atributos también, usando literales o variables. Para los atributos de texto la sintaxis es muy parecida a HTML.


#![feature(plugin)]
#![plugin(maud_macros)]

extern crate maud;

fn main(){
let name = "Adrián";
let amazon = "http://www.amazon.com";
let markup = html!{
p { "Hola, soy " (name) " y estoy usando Maud" }
p {
"Este texto contiene enlaces a "
a href="http://www.google.com" "Google"
" y a "
a href=(amazon) "Amazon"
}
};
println!("{}", markup.into_string());
}


Además existen en HTML atributos vacíos. Es decir, atributos que con su sola presencia basta y normalmente no llevan valor asignado.


#![feature(plugin)]
#![plugin(maud_macros)]

extern crate maud;

fn main(){
let name = "Adrián";
let allow_editing = true;
let markup = html!{
p { "Hola, soy " (name) " y estoy usando Maud" }
p contenteditable?[allow_editing] {
}
};
println!("{}", markup.into_string());
}


En este caso el atributo contenteditable se añade si la variable allow_editing es true. Si queremos añadir atributos vacíos en cualquier circunstancia podemos simplemente quitar [allow_editing] y dejar p contenteditable? {}.

Los IDs y las clases se añaden usando la sintaxis esperable, puntos y almohadillas.


#![feature(plugin)]
#![plugin(maud_macros)]

extern crate maud;

fn main(){
let name = "Adrián";
let markup = html!{
p { "Hola, soy " (name) " y estoy usando Maud" }
p.red.font-big#editor contenteditable? {
}
};
println!("{}", markup.into_string());
}


Estructuras de control


Maud soporta las estructuras de control básicas de Rust, if/else, if/let, for in y match.


#![feature(plugin)]
#![plugin(maud_macros)]

extern crate maud;

fn main(){
let loggedIn = false;
let email = Some("mariscal@example.com");
let fonts = ["Arial","Times New Roman","Verdana"];
let markup = html!{
@if loggedIn {
h1 { "Has iniciado sesión" }
} @else {
h1 { "Por favor, inicia sesión primero" }
}

@if let Some(mail) = email {
p { "Su email es " (mail) }
}
ol {
@for font in &fonts {
li (font)
}
}

};
println!("{}", markup.into_string());
}


Como vemos, Maud posee suficientes características para ser interesante. Maud además permite extender el motor para representar cualquier tipo de dato. Por defecto Maud mirá si está implementado std::fmt::Display pero si queremos añadir etiquetas extra este método no funcionará. En cambio si se implementa la trait maud::Render tendrás control total sobre como va a mostrar Maud las variables de ese tipo. En la crate maud_extras se encuentran implementaciones por ejemplo de Markdown para Maud.

Maud se integra además con web frameworks de Rust, en especial con Iron y con Rocket. Sin duda, una de mis crates favoritas.
Tags: programacion rust template html crate razor web plantilla web-framework maud