Documentación con Asciidoctor: buena, bonita y barata
Índice de contenidos
- 1. Introducción
- 2. Entorno
- 3. Instalación para uso standalone
- 4. Instalación como plugin de Maven
- 5. Creación de documentos .adoc
- 6. Conclusiones
1. Introducción
Existen muchas opciones a la hora de escribir documentación, desde soluciones basadas en Markdown y LaTeX hasta complejos procesadores de texto tipo Word. En este tutorial vamos a ver Asciidoctor, una herramienta open source basada en la sintaxis de AsciiDoc que, además de convertir el contenido a diferentes formatos, incluye funcionalidades muy útiles como es el poder referenciar una sección de código de otro fichero. Una joya, vamos.
Pero… ¿qué es AsciiDoc? Para los que no lo conozcan, AsciiDoc es un formato de escritura en texto plano que puede convertirse a otros formatos como HTML, PDF o ePub. Lo que viene siendo un primo hermano de Markdown.
Asciidoctor procesa texto de tipo AsciiDoc y lo convierte con sus propias plantillas para producir un documento HTML5 muy majete, además de DocBook 5 y otros formatos. Y por si fuera poco, el html que obtenemos es totalmente responsive. Pero si el estilo aplicado por Asciidoctor no te convence, puedes definirte uno propio igual que puedes implementar tu propio conversor para obtener otros formatos.
La propia documentación de la herramienta (además de bonica, obviously) es muy completa y en ella podemos encontrar (en inglés) cómo hacer todo aquello que se nos ocurra, o casi. Aquí veremos dos alternativas de uso y algunos ejemplos.
¡Ah! Y por si os lo estáis preguntando: sí, este tutorial está hecho con Asciidoctor 😉
2. Entorno
El tutorial está escrito en el siguiente entorno:
-
Hardware: Portátil MacBook Pro 15’’ (2.3 GHz Intel i7, 8GB 1333 Mhz DDR3, 500GB Solid State SATA Storage)
-
Sistema Operativo: Mac OS X Yosemite 10.10.3
-
Java Virtual Machine (JVM) 8
3. Instalación para uso standalone
Asciidoctor se puede instalar vía el comando gem de RubyGems o con el gestor de paquetes correspondiente si estamos en Linux (los detalles están en la web de Asciidoctor). Aquí veremos la instalación con RubyGems para Mac OS X.
Si no tenemos RubyGems podemos instalarlo con Homebrew, lo que a su vez necesita tener instalado XCode. Si no tenemos nada de esto instalado, hemos de seguir lo siguiente.
Primero descargamos e instalamos XCode. Después abrimos XCode para aceptar los términos de licencia y permitir que instale el resto de componentes que necesita.
Después, instalamos las herramientas para línea de comandos de XCode e instalamos Homebrew, comprobando que no haya habido errores en el proceso.
$ xcode-select --install $ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" $ brew doctor
Ahora que ya tenemos Homebrew, lo utilizamos para instalar RubyGems que es con lo que instalaremos Asciidoctor. Después configuramos éste para que instale los ejecutables en /usr/local/bin y así tenerlos accesibles en el PATH.
$ brew install ruby $ echo "gem: -N -n /usr/local/bin" >> $HOME/.gemrc
Por último, ya podemos instalar Asciidoctor y comprobar que funciona correctamente:
$ gem install asciidoctor $ asciidoctor -v Asciidoctor 1.5.2 [http://asciidoctor.org] Runtime Environment (ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin14]) (lc:UTF-8 fs:UTF-8 in:- ex:UTF-8)
Para procesar nuestro documento .adoc hacemos la llamada:
$ asciidoctor midocumento.adoc
4. Instalación como plugin de Maven
Si vamos a usar Asciidoctor dentro de un proyecto Maven podemos usar un plugin para procesar los .adoc en vez de la herramienta standalone. Las instrucciones paso a paso se encuentran en: http://asciidoctor.org/docs/asciidoctor-maven-plugin/, pero en resumen basta con añadir lo siguiente en el pom.xml:
pom.xml
<properties> <asciidoctor.version>1.5.2</asciidoctor.version> </properties> ... <build> <plugins> ... <plugin> <groupId>org.asciidoctor</groupId> <artifactId>asciidoctor-maven-plugin</artifactId> <version>${asciidoctor.version}</version> <executions> <execution> <id>output-html</id> <phase>generate-resources</phase> <goals> <goal>process-asciidoc</goal> </goals> <configuration> <sourceDirectory>src/main/doc</sourceDirectory> <outputDirectory>target/docs</outputDirectory> <backend>html</backend> <sourceHighlighter>coderay</sourceHighlighter> </configuration> </execution> </executions> </plugin> ... </plugins> </build>
Vemos que en la parte de configuración podemos indicar el directorio fuente donde vayan a estar los .adoc, que por defecto es /src/main/adoc (¡ojo! Ese directorio es el que dentro del .adoc se toma como base, importante a la hora incluir código externo como veremos más adelante), el directorio de destino de los ficheros convertidos, el formato de conversión (en el ejemplo html), o la librería a utilizar si queremos resaltar la sintaxis del código (por el momento, sólo están disponibles coderay y highlightjs). La lista completa de opciones de configuración la podemos encontrar en la propia web de instalación.
5. Creación de documentos .adoc
Como hemos dicho, AsciiDoc no es más que texto plano y por lo tanto podemos usar nuestro editor de texto preferido para crear un .adoc . Y si queremos resaltar la sintaxis de AsciiDoc, existen plugins para diferentes editores. Por ejemplo, para Sublime tenemos este plugin: https://github.com/SublimeText/AsciiDoc.
5.1. Cabecera, títulos e índice
La cabecera (o header para cuando consultéis la documentación en inglés) del documento .adoc es donde pondremos los distintos atributos que queramos usar. Comienza con el título y termina con la primera línea en blanco. Como ejemplo, veamos la cabecera de este tutorial:
= Documentación con Asciidoctor: buena, bonita y barata :hardbreaks: :toc: :toc-title: Índice de contenidos :sectnums: :source-highlighter: coderay
La primera línea es el título, que se indica con =. En el resto del documento, el subtítulo se indicaría con ==, el subsubtítulo con ===, y así podemos ir añadiendo niveles a base de añadir =.
Por defecto, las líneas adyacentes se consideran parte del mismo párrafo a la hora de convertir el documento. Si queremos preservar el salto de línea en todo el documento debemos añadir el atributo :hardbreaks:. Si sólo queremos aplicar esto a determinados bloques, en vez de usar el atributo añadiremos [%hardbreaks] antes del bloque correspondiente.
[%hardbreaks] Esto es una línea. Y esta es otra línea distinta.
Los dos siguientes atributos de nuestro ejemplo, :toc: y :toc-title: hacen referencia a la tabla de contenidos o índice. Con :toc: indicamos que queremos incluir el índice, que por defecto se colocará bajo el título. Si queremos que aparezca en un lateral, por ejemplo el izquierdo, pondremos:
:toc: left
Con :toc-title: podemos modificar el título, que por defecto es Table of Contents.
El atributo :sectnums: sirve para que nos numere las distintas secciones (títulos) del documento.
Por último, con :source-highlighter: se resalta la sintaxis en los ejemplos de código que tengamos con la librería que indiquemos. Este atributo sólo es necesario si usamos Asciidoctor como standalone, ya que si lo usamos a través del plugin de Maven se indica en la configuración del plugin como hemos visto antes.
5.2. Formateo de texto
En este aspecto hay poco que destacar ya que la sintaxis es muy similar a otros markdowns:
*Esto* saldrá en negrita. _Esto_ lo hará en cursiva. Los subíndices van entre ~, como por ejemplo: H~2~O. Los superíndices, en cambio, van entre ^, como en E=mc^2^. Y si queremos comillas, las tenemos "`dobles`" y '`simples`'.
5.3. Listas
Las listas pueden ser ordenadas o no, en cada caso lo que cambia es el carácter al principio de cada elemento.
Listas no ordenadas
Para listas no ordenadas, usaremos ‘*’ :
.Autores de literatura fantástica * Terry Pratchett * J.R.R. Tolkien * George R.R. Martin
Como vemos, podemos añadir un título a la lista anteponiéndolo a los elementos y comenzando con un ‘.’. Para añadir niveles a la lista (lista enlazada), basta con añadir más ‘*’ al principio del elemento hasta un límite de 5 (número máximo de niveles):
* nivel 1 ** nivel 2 *** nivel 3 **** nivel 4 ***** nivel 5 * nivel 1
Listas ordenadas
En las listas ordenadas, el carácter inicial de cada elemento es el ‘.’ y los elementos quedarán numerados en el formato final:
.Lista ordenada de pasos . Paso 1 . Paso 2 . Paso 3
Como en las listas ordenadas, tenemos también la opción de añadir un título a la lista y las listas enlazadas se consiguen añadiendo al elemento los ‘.’ necesarios para el nivel queremos conseguir. Además, si queremos que la lista ordenada comience en un número distinto del 1, podemos indicarlo añadiendo [start=4] (si quisiéramos comenzar en 4, por ejemplo).
5.4. Inclusión de bloques de código
Si queremos incluir un bloque de código en nuestra documentación, lo podemos hacer de la siguiente manera:
[source,ruby] .app.rb ---- require 'sinatra' get '/hi' do "Hello World!" end ----
Veamos en detalle qué es cada cosa. La primera línea indica que lo que sigue a continuación es un bloque de código, que se indica con el valor source y especificando el lenguaje del código, en este caso ruby. El bloque queda limitado por —- a su inicio y final. Opcionalmente, antes de comenzar el bloque en sí, podemos añadir un título al que se le añade un . al inicio. En nuestro ejemplo, el título que aparecerá será app.rb .
Además de los bloques de código (source), existen otros tipos de bloques como los literal (muestra el texto del bloque tal cual, sin procesar) o los quote (para citas) donde cambia no sólo el nombre del bloque sino el carácter delimitador. La lista completa de opciones puede consultarse en http://asciidoctor.org/docs/user-manual/#delimited-blocks.
Bloques de código desde fichero
Si queremos incluir una sección de código (de otro fichero) en el .adoc, debemos envolver dicha sección de código del fichero correspondiente entre comentarios que sigan el formato tag::<nombre_tag>[] … end::<nombre_tag>[]. Ejemplo:
Ejemplo de tags para inclusión de código en un fichero llamado core.rb
# tag::timings[] if timings timings.record :read timings.start :parse end # end::timings[] # tag::parse[] doc = (options[:parse] == false ? (Document.new lines, options) : (Document.new lines,options).parse) timings.record :parse if timings doc # end::parse[]
En el .adoc la referencia a la primer sección de código se hace de la siguiente forma:
[source, ruby] ---- include::core.rb[tag=timings] ----
Y si queremos incluir las dos secciones lo podemos hacer usando tags en vez de tag:
[source, ruby] ---- include::core.rb[tags=timings; parse] ----
Si el fichero del cual queremos extraer una sección de código se encuentra en un directorio diferente al de nuestro .adoc, podemos indicar la ruta entera en vez de únicamente el nombre. Pero para eso, lo más práctico es añadir una variable con la ruta en la cabecera del documento y ahorrarnos el tener que escribir la ruta entera cada vez. Por ejemplo, en la cabecera añadimos un atributo con la ruta:
:sourcedir: src/main/
Y a la hora de incluir el código externo ponemos:
[source, ruby] ---- include::{sourcedir}/core.rb[tags=timings; parse] ----
5.5. Cuestión de estilo
Como hemos visto, en principio Asciidoctor aplica su propio estilo (su propio .css) al html generado, y por defecto lo añade embebido en el propio html. Pero este comportamiento lo podemos cambiar fácilmente, siguiendo unos u otros pasos dependiendo de dónde lo hagamos.
Configuración en la cabecera
Si en vez de tener el estilo embebido en el html queremos tenerlo en un fichero a parte y enlazar a él desde el html sólo tenemos que añadir el atributo :linkcss: a la cabecera de nuestro .adoc.
Si lo que queremos es usar nuestro propio CSS en vez del de Asciidoctor hemos de añadir en la cabecera el atributo :stylesheet: indicando la ruta y nombre de nuestro fichero CSS:
:stylesheet: src/main/resources/css/miEstilo.css
Pero si no queremos aplicar ningún estilo y buscamos obtener el html “en crudo”, basta con poner:
:stylesheet: false
Configuración en el plugin de Maven
Si estamos usando el plugin de Maven, en vez de configurar los estilos dentro del propio documento podemos hacerlo en la configuración del plugin. Esto nos permite cambiar los estilos sin necesidad de alterar el .adoc, incluso podríamos definir distintas ejecuciones del plugin variando las opciones de estilos y ejecutar una u otra según nos interese. Para ello, en el pom.xml, podemos cambiar la configuración del estilo añadiendo los atributos correspondientes:
pom.xml con dos ejecuciones distintas definidas, una con estilo propio y otra sin estilo ninguno
<executions> <execution> <id>html-and-myStyle</id> ... <configuration> ... <attributes> <linkcss>true</linkcss> <stylesheet>src/main/resources/css/miEstilo.css</stylesheet> </attributes> ... </configuration> </execution> <execution> <id>only-html</id> ... <configuration> ... <attributes> <stylesheet>false</stylesheet> </attributes> ... </configuration> </execution> </executions>
Mediante el atributo linkcss indicamos si queremos que la información de estilo se añada a través de un enlace a un fichero a parte (valor true) o embebida en el html (con el valor false o directamente omitiendo el atributo). Con stylesheet podemos cambiar el .css a utilizar (si ponemos como valor la ruta del fichero) o indicar que no queremos ningún estilo (usando false como valor).
Configuración desde línea de comandos
Si estamos usando la herramienta standalone en vez del plugin de Maven pero no queremos añadir la información sobre los estilos en el propio documento (ya hemos visto las ventajas en el caso de Maven), podemos hacerlo en la llamada al proceso asciidoctor.
Para indicar que queremos incluir el estilo como enlace en vez de embebido:
$ asciidoctor -a linkcss midocumento.adoc
Si queremos usar un estilo propio:
$ asciidoctor -a stylesheet=src/main/resources/css/miEstilo.css midocumento.adoc
Y si no queremos usar ningún estilo:
$ asciidoctor -a stylesheet! midocumento.adoc
6. Conclusiones
Asciidoctor es un procesador que nos permite obtener, a partir de texto plano, documentación en varios formatos con un estilo más que aceptable y responsive “out of the box”. La sintaxis de AsciiDoc es sencilla y bien documentada, y sirve no sólo para artículos sino para libros y otros tipos de documentos. La posibilidad de incluir código automáticamente desde otros ficheros, lo hace una herramienta muy adecuada para la documentación de software, ya que nos permite tener los ejemplos de código siempre actualizados a la vez que dejamos constancia, en el propio código, de que se está haciendo una referencia desde la documentación (con lo que si modificamos ese código veremos que debemos recompilar la documentación). Así es más fácil mantener todo actualizado.
Gracias por el aporte, me pareció muy interesante… Lo intentaré apenas pueda quiero ver qué tanto puedo hacer con ellos. Saludos!
Muy buena introducción. Cabe recomendar asciidocfx