domingo, 15 de noviembre de 2009

Herramientas de documentación en Perl

[English translation]
Perl tiene su propio formato de documentación llamado POD (del inglés Plain Old Documentation), este formato es estructurado y fué específicamente diseñado para ser manipulado fácilmente. POD se usa no solo como herramienta para la documentar Perl, sino como lenguaje de Wiki y hasta para escribir libros.

En perl la herramienta más conocida para leer la documentación es perldoc, que funciona de manera muy similar al manual de unix man(1), así cuando queremos ver la documentación de un módulo podemos hacer:

$ perldoc IO::Handle

Lo que nos mostrará el manual de IO::Handle, también podemos obtener el manual del módulo en otra variedad de formatos, por ejemplo, lo podemos guardar en formato "man", HTML o incluso LaTeX:

$ perldoc -T -o LaTeX IO::Handle > IO::Handle.tex
$ perldoc -T -o html IO::Handle > IO::Handle.html

Si vemos el HTML generado nos daremos cuenta que los enlaces generados al resto de la documentación se refieren al CPAN, pero esto es solo la forma en la que funciona perldoc, existen cientos de módulos para procesar POD, que permiten manipulaciones avanzadas y convertirlo a HTML, XML, LaTeX, texto y DocBook, entre otros.

Cuando se necesita mas control sobre la generación de los documentos, puedes usar otras herramientas como: pod2html y pod2latex que permiten crear documentos basados en varios archivos POD que se procesan en conjunto, por ejemplo para hacer un libro, donde cada capítulo se guarda en un POD diferente.

Si necesitas control total sobre la conversión de POD, siempre puedes programar utilizando los módulos disponibles en el CPAN, y uno de los más fáciles de usar es Pod::Simple, que ofrece varias conversiones predefinidas, por ejemplo si quieres generar HTML en una aplicación CGI, puedes hacerlo con facilidad:

1 use CGI;
2 use Pod::Simple::HTML;
3 
4 my $q = new CGI;
5 my $parser = Pod::Simple::HTML->new;
6 $parser->output_fh(*STDOUT);
7 
8 print $q->header("text/html");
9 $parser->parse_file("/usr/share/perl/5.8/IO/File.pod");

Este programa inicializa los objetos CGI y Pod::Simple::HTML (líneas 4 a 6), envía los encabezados de HTTP (línea 6) y finalmente envía el documento en HTML (línea 9).

En este caso debes conocer el nombre exacto del POD que deseas enviar, sin embargo si quieres saber el nombre de un archivo que contiene información sobre un módulo en particular, debes buscarlo, ¿donde?, pues lo más recomendable es buscarlo en los mismos sitios donde perl buscará los módulos y los ejecutables.

La variable @INC contiene los lugares donde perl busca los módulos que usamos en nuestros programas, esta es una combinación de lugares predefinidos al momento de compilar perl , el contenido de la variable de ambiente PERL5LIB y los lugares epecificados con "use lib" en el código de programas y módulos. Por otra parte cuando perl debe ejecutar un programa lo busca en la variable de ambiente PATH, así que para encontrar el archivo que contiene el POD de un módulo o programa en Perl  podemos usar una función como find_pod que se muestra a continuación:

 1 use Modern::Perl;
 2 use Env::Path;
 3 use File::Spec::Functions;
 4 
 5 sub find_pod
 6 {
 7     my $module = shift;
 8     my @module_path = split("::", $module);
 9     for my $dir ( @INC, Env::Path->PATH->List ) {
10         for my $ext ( '', '.pod', '.pm', '.pl' ) {
11             my $name = catfile($dir, @module_path) . $ext;
12             return $name if -e $name;
13         }
14     }
15     return undef;
16 }
17 
18 print "Nombre: ", find_pod(@ARGV), "\n";

Esta función recibe el nombre del módulo o programa, y lo separa por "::", luego itera todos los directorios que se encuentran en @INC y el PATH del sistema que convertimos en una lista con "Env::Path->PATH->List" en la línea 9, para cada directorio se buscan los nombres y los nombres aumentados con las extensiones: pod, pm y pl, el primer nombre encontrado es retornado, y si no se consigue nada se retorna undef.

Nótese el uso de "Env::Path" para obtener el path del sistema de manera portable y "File::Spec::Functions" que importa la función "catfile" para generar los nombres de archivo permite concatenar un nombre de archivo de manera portable, haciendo que esta subrutina funcione en unix y windows.

Sin embargo, hice esto solo por diversión, porque en CPAN ya hay algo mucho mejor: "Pod::Simple::Search", que está bien hecha, se puede instalar en cualquier lado y es mucho más flexible que la subrutina de juguete del ejemplo anterior, así que nuestro CGI para mostrar documentación en POD queda así:

 1 #!/usr/bin/perl
 2 use CGI;
 3 use Pod::Simple::HTML;
 4 use Pod::Simple::Search;
 5 
 6 my $q = new CGI;
 7 my $parser = Pod::Simple::HTML->new;
 8 $parser->output_fh(*STDOUT);
 9 
10 my $filename = Pod::Simple::Search->new->inc(1)->find($q->param("pod"));
11 print $q->header("text/html");
12 $parser->parse_file($filename);

Si tenemos un servidor web configurado, solamente copiamos el programa en el directorio apropiado para ejecutar CGI con el nombre perldocweb, lo marcamos como ejecutable, y todo debería funcionar, para probarlo podemos visitar:

http://localhost/cgi-bin/perldocweb?pod=IO::File

lo que muestra el manual de IO::File en el navegador, aunque todavía los enlaces apuntan al CPAN, si quieres que los enlaces apunten al mismo servidor de documentación hay que establecer el prefijo para generar los enlaces (perldoc_url_prefix), para ello utilizaré el método url() de CGI, que usado como se muestra en la línea 12 retorna el URL completo del script sin el query:


 1 #!/usr/bin/perl
 2 use CGI;
 3 use Pod::Simple::HTML;
 4 use Pod::Simple::Search;
 5 
 6 my $q = new CGI;
 7 my $parser = Pod::Simple::HTML->new;
 8 $parser->output_fh(*STDOUT);
 9 
10 my $filename = Pod::Simple::Search->new->inc(1)->find($q->param("pod"));
11 print $q->header("text/html");
12 $parser->perldoc_url_prefix($q->url(-path_info=>1) . "?pod=");
13 $parser->parse_file($filename);

Nada mal, un servidor de documentación sencillo en 13 líneas, en el próximo artículo veremos como convertir POD a otra cantidad de formatos, mientras tanto pueden instalar Pod::Server y ver como se hace esto con mucha más elegancia.

No hay comentarios:

Publicar un comentario