CaFe Perl v0.3 - Periódico de la Comunidad Perl de Capital Federal

Editorial

Este mes hay poca editorial y un poco más de contenido. Simplemente quiero agradecer a Manuel Sanguino por la ayuda en la traducción de la entrevista PERLitas de este mes, a Matías Palomec por el ``cacho'' de código para POC y a Leonardo Pigñer por la revisión de las ``Mordiditas de aquí y de allá''.

Hay una innovación, para el alivio de más de uno, y es que la sección dedicada a ``CaFe Perl en Perl'' ya no existe más. En su lugar esa parte de la publicación va a estar dedicada a temitas sueltos, de esos que uno charla continuamente con los que comparte la vida, esos de las charlitas de café ... y gracias a Matías que aportó un código propio para publicar ha sido bautizada como POC (Peace of Code).

Espero que lo disfruten. Hasta la próxima taza de CaFe Perl !!! ... eso sí, café del bueno ;-).

Víctor A. Rodríguez (Bit-Man)

PERLitas

OpenInteract es un servidor de aplicaciones web escrito en Perl. Este ofrece persistencia de data integrada, administración de usuarios y grupos de usuarios, mas una vía fácil para crear y distribuir completamente bases de datos de aplicaciones independientes.

Hola Cris, por favor una presentación para el grupo CaFe.pm

Mi nombre es Cris Winters y actualmente vivo en Pittsburg, Pennsylvania Estados Unidos. He estado casado por 5 años con mi persona favorita en el mundo.

He estado usando Perl por aproximadamente 10 años o mas, los primeros dos años fue por encima y a tiempo completo desde 1997.

Actualmente trabajo en Vocollect (http://www.vocollect.com/) haciendo aseguramiento de calidad de software (software quality assurance) sobre un producto Java basado en web; Pronto me mudaré a otra parte de la compañía para hacer más desarrollo embebido junto con la confección de sistemas de workflow personalizados

Cuáles son tu áreas de experiencia ?

Bueno yo no debería de llamarme experto en nada :-) Sin embargo estuve trabajando en el desarrollo de arquitectura y aplicaciones web por mas de ocho años y en frameworks por seis años. También he desarrollado sistemas de workflow bastante genéricos y herramientas de mapeo objeto-relacional (object-relational mapping) todo este tiempo.

Uno de los aspectos interesantes de la arquitectura web es que si lo haces de forma correcta las ideas básicas se transfieren muy bien a otros lenguajes. En los últimos cuatro años mi trabajo pago ha sido escribir código en Java tanto para aplicaciones web como como procesos side-server en general. Y mucha de las ideas que aprendí de Perl también fueron implementadas en Java, aunque con mucho mas código !.

Qué lo impulsó a construir OpenInteract?

OpenInteract vino casi de la misma manera como creo mucho de los otros frameworks aparecieron. Uno construye un sistema para un cliente, lo instala y funciona bárbaro. Entonces uno construye otro sistema para otro cliente, que no es exactamente el mismo, pero lo suficientemente como para copiar-y- pegar y los conseguís para la ajustada fecha límite.

Entonces uno construye otro sistema que difiere de los anteriores, y a la vez uno hace los mismos cambios que se necesitan aplicar a los dos anteriores. Entonces se empieza a encontrar concordancias entre ellas y construir un sistema que reuna estas necesidades.

Hacé esto diez o mas veces y tendrás un Framework!!!

Lo que también puede ser interesante de observar es que el sistema detrás de Kuro5hin (http://kuro5hin.org/) también liberado desde esta misma compañía cuando estos fueron desarrollados la primera vez tenían bastantes pocas cosas en común, pero estos rápidamente divergieron debido sus diferentes especialidades.

Que problemas resuelve OpenInteract ?

OpenInteract toma a su cargo mucha del trabajo de plomería (infraestructura ??) que hay que hacer en la aplicaciones web :

Como una servidor de aplicaciones (application server) no resuelve la problemática para los no desarrolladores, o por lo menos no los interesantes. Hay aplicaciones simples de ejemplo instaladas con el server, pero nada como Slashdot, un sistema de webmail o un CMS más sofisticado (Content Management System)

Lo que hace OpenInteract es facilitar a los desarrolladores el crear ese tipo de aplicaciones complejas.

Probablemente vale la pena hacer notar la diferencia entre OpenInteract v1.x y 2.x. OpenInteract 2.x es casi una completa re-escritura y tiene un mejor diseño y documentación. Si estás comenzando recomiendo fuertemente que mires ahí primero, aunque aún está en beta.

Qué consejo le daría a los futuros diseñadores ??

Cada uno aprende en forma diferente así que es difícil dar un consejo genérico. Pero en mi experiencia muchos desarrolladores aprender mejor haciendo. Y el open source nos da mucho con que trabajar ! Y con algo como OpenInteract es aún más fácil porque no hay que recompilar nada se puede modificar un package, rearrancar el server y ver qué pasa. Algo del mejor aprendizaje puede suceder rompiendo cosas a propósito (o aún accidentalmente).

Otra parte importante es que es duro cuando no se tiene trabajo qué hacer. Puedo decir que vayas por tal o cual código, pero no creo que realmente se te pegue a menos que tengas algo qué hacer con él.

Así que pensá que cosas te interesan y avanzá con una aplicación simple que puedas escribir que las incluya. Por ejemplo, te puede gustar el observar aves y quiera crear una bitácora de tus avistamientos. O tal vez coleccionás revistas cómicas y querés una base de datos mejor para almacenar la información junto con imágenes de las portadas. O guardar tus recetas favoritas. No tiene que ser algo muy complicado, sólo algo en lo que estés realmente interesado.

En qué partes del código le aconsejaría mirar al novato en Perl , para tener un aprendizaje placentero ?

OpenInteract tiene mucho código e imagino que puede ser bastante intimidante, especialmente porque es un framework de aplicaciones que necesita tener hooks para muchas funcionalidades que la gente podría no necesitar nunca. Sin embargo, lo que podría ser interesantes es seguir el camino de un requerimiento según se mueve a través del server. Uno de los documentos que viene con OpenInteract2 (OpenInteract2::Manual::Architecture) tiene una sección Tracing a Request. Este te lleva a través del sistema en un alto nivel, y siguiéndolo como abre distintos módulos puede ser muy educativo (en verdad, mientras se lee esa sección probablemente será muy útil tener muestras del código de los diferentes módulos que aparecen)

Cuáles fueron las limitaciones impuestas por Perl al proyecto ?

Ninguna que pueda recordar, es decir no recuerdo haber pensado ``Me gustaría que Perl tuviera X''. Unas pocas posibilidades :

Vió las características de Perl 6 ??

Muy poco. Se siente como Perl pero con mayor planificación, menos características por deposición. Pero sólo es una primer impresión.

Tiene alguna opinión o consejo acerca de él ??

Soy ambivalente. Como cualquiera quisiera que estuviera acá ahora mismo, particularmente por toda la atención hacia Ruby y, en menor cantidad, a Pyhton. Creo que el sistema OO de Perl 5 previno a mucha gente de bien usarlo o apegarse a él e introdujo la limpieza y su revisión. Dicho esto, no tenemos una gran pila de dinero para darle a la gente inteligente que diseña e implementa Perl6 y se cuán difícil es balancear la programación open source y un trabajo full-time (sin mencionar una familia, hobbies no técnicos, sueño ...). También, estoy aterrorizado por el progreso que Autrijius hizo en tan poco tiempo con Pugs (Perl 6 corriendo en Haskell). Es realmente sorprendente. Ojalé tuviera algo de tiempo de sobra para aprender más sobre él y ayudar.

Por qué cree que Perl no es tan ampliamente usado para el desarrollo de aplicaciones ??

Hay algunas razones, pero las primeras que me vienen a la mente :

Java tiene tipos de datos estáticos, y ya que IDEA conoce mucho sobre Java hace fácil el renombrar un método llamad execute() en una clase Foo sea donde sea llamada, ignorando execute() en la clase Bar. O reemplazar todos los constructores con dos argumentos con la llamada a un solo método estático. O cambia el orden de los argumentos en un método. O muchas, muchas otras cosas. Es difícil explicar qué útil es esto, pero a un nivel profundo hace al código mucho más maleable porque uno sabe que cambiarlo es tan simple, lo mismo que el crear buenos de test unitarios le dan a uno la confianza de hacer cambios funcionales en el código (algunas de estas características están presentes en Emacs a través de J2EE, pero IMO no son tan usables ni potentes).

Algunos aspectos de Perl hacen a esta clase de herramientas difíciles, pero espero que Perl 6 habilitará el uso de este tipo de herramientas.

Cómo se puede colaborar con OpenInteract ??

Depende de lo que quieras hacer !

Por ejemplo, si se es un buen diseñador gráfico hay algo de trabajo a hacer relacionado con la falta de stylesheets (hojas de estilo) y técnicas de layout modernas en OpenInteract2. También queremos hacer el site de OpenInteract (http://www.openinteract.org/) un poco más amigable y con una vista más agradable.

O si se es un desarrollador de aplicaciones se puede contribuir con ideas o implementaciones. O aún mejor, se puede desempeñar en una de las actividades más subvaluados: como usuario inteligente. Muchas veces la gente usa un proyecto open source pero algo del código no funciona de la forma esperada, o una interfaz es un poco dura de usar, o la documentación es confusa. Y no lo dicen al proyecto porque no quieren verse involucrados o no quieren ser un problema.

Pero esto es información de mucho peso. Es fácil para la gente que trabaja en proyectos grandes el olvidarse como las personas lo usan, así que sus palabra, aún para algo simple, probablemente sean más útiles de lo que se piensa. Y aún si lo que dirigen el proyecto no están de acuerdo aún sigue siendo útil para ellos saber cómo se sienten para así modificar la documentación y llegar a lo que la gente espera, agregar puntos a la FAQ para enviar a la gente al lugar correcto, u otros cambios.

Si estás usando OpenInteract2 y algo es confuso, entonces hacénoslo saber !

Finalmente, ya que OpenInteract2 tiene soporte para internacionalización también necesitamos gente para traducir nuestros mensajes en Inglés a otros idiomas.

... y las habilidades mínimas requeridas ??

Creo que existe el trabajo suficiente para todo tipo de habilidades! Además, uno de los beneficios del open source es que se puede aprender mientras se hace.

Qué características cree que no están presentes, y cuáles agregará en el corto plazo ??

Cualquier idea que cualquier persona tenga sobre características o fixes deberá estar en nuestro sistema de seguimiento de problemas JIRA (http://jira.openinteract.org/). De lo contrario se cae por el costado y nunca se implementa.

Algún módulo favorito de CPAN ??

No hay forma de construir algo como OpenInteract sin CPAN y hay muchos módulos que use para hacer cosas simples y las hizo tan bien que nunca tuve que volver a pensar sobre ella. Sin embargo, de los que primero se me vienen a la mente, acá hay una lista de módulos que uso más frecuentemente para resolver los problemas más comunes :

También uso un par mios que uso frecuentemente (Class::Factory and Class::Observable), pero no es junto ponerlos en la lista de favoritos :-)

Tiene alguna experiencia (divertida o no tanto) que le ocurrió mientras estaba haciendo OpenInteract y que quiera compartir con nosotros ??

Unas pocas cosas vienen a mi mente.

La compañía desde donde OpenInteract surgió fue lo suficientemente generosa para hacerla open source. Creo que al final saldrán beneficiados de esto pero fue un gran honor (No tengo que poner un gran esfuerzo para hacerlo, sólo un poco).

Hace poco más de un año le pregunté a la comunidad por fondos para poder comprar un server y poner el website de OpenInteract y el site de JIRA, junto con mi website :-) No me llenaron de dinero pero lo que obtuve me sorprendió, y fue altamente apreciado.

Finalmente, tuve una gran sorpresa cuando alguien me mandó un e-mail para decirme que usaron OpenInteract para un proyecto y que estaba funcionando bien. De hecho, hace cerca de año y medio oí de una compañía en Alemania que tenía algo de dinero extra en su presupuesto. Me hicieron volar hasta ahí e hice un entrenamiento con los desarrolladores y aprendí acerca de su sistema. Fue un gran placer.

SudorNews

Job Description : Serán responsables de la instalación y soporte de centrales VOIP.

Job Requirements : Incorporaremos un administrador Unix/Linux. Con conocimientos de programación en shell script, Perl y MySQL. Preferible contar con nociones de Asterisk, VOIP y SIP.

http://www.usla.org.ar/modules/jobs/index.php?pa=viewlistings&lid=74

Job Description : Empresa de servicios profesionales con sede en EE.UU. que también opera en Argentina busca administradores de sistema calificados con residencia en el cono sur de Sudamérica. Necesitamos incorporar de manera inmediata administradores de sistemas Unix.

Job Requirements : Los candidatos deben tener un excelente conocimiento de sistemas Unix (especialmente Linux) y experiencia en el manejo de Apache Web server, Sendmail y otros MTAs, Administración de bases de datos (especialmente MySQL, PostgreSQL), Seguridad y administración de firewall. De preferencia, experiencia de trabajo con Zope.

http://www.usla.org.ar/modules/jobs/index.php?pa=viewlistings&lid=73

Para importante empresa de servicios y contenidos de telefonía celular, solicitamos la postulación de profesionales graduados de las carreras de Tecnología, preferentemente con experiencia en empresas de Telecomunicaciones o Internet, para incorporarse al área de Desarrollo de Sistemas en el Centro de mensajería de texto. Valoraremos los conocimientos de PHP, Mysql, Perl y Linux; los idiomas inglés y portugués son requeridos para esta posición. Las principales responsabilidades serán: la relación con los operadores celulares, desarrollo de proyectos y mantenimiento de plataformas, deberá coordinar el área técnica y monitorear las acciones operativas del día a día.

Los interesados deberán aplicar su CV sin omitir remuneración pretendida

http://www.bumeran.com.ar/aplicantes/empleos/1075520.html

Incorporaremos a nuestro equipo de tecnología un desarrollador semi-senior. Los requisitos para postular son Experiencia comprobable en programación PHP, SQL y HTML. Conocimientos de desarrollo en ambientes Linux, lenguajes PERL, Bash, etc.

Se valorará conocimientos en Oracle, XML. Disponibilidad full time or part- time.

http://www.bumeran.com.ar/aplicantes/empleos/1074830.html

Experiencia actualizada y comprobable en administración de sistemas Unix ( AIX, Solaris, Linux) con continuidad mínima de tres años. Conocimientos de bases de datos relacionales de envergadura, en particular Oracle e Informix. Conocimientos de programación en Shell Script, PERL. Conocimientos de herramientas de monitoreo y de herramientas de backup corporativo. No postularse si no se cumplen estrictamente los requerimientos establecidos.

http://www.bumeran.com.ar/aplicantes/empleos/1067720.html

SMS - San Martín, Suarez y Asociados - www.sms.com.ar Nuestro cliente una Compañìa de servicios de Telecomunicaciones Busca: Data Base Administrator (DBA) Buscamos especialista en DBA para administrar las bases de datos de la compañía. La posición requiere del diseño de nuevas soluciones, el mantenimiento, monitoreo y dimensionamiento de las mismas. Se valoran conocimientos en Administración de MS SQL7/2000 - POSTGRESQL y MYSQL, en Scripting para web (PERL/PHP) y shell scripting (BASH/CSH, Perl), lenguajes ANSI SQL, Pg/SQL, T-SQUL, PI-SQL. Serán altamente valorada experiencia en servicio de voz sobre IP. Apuntamos a un Lic o Ingeniero en Sistemas que le agrade experimentar con distintas tecnologías, sea creativo, orientado a la resoluciòn de problemas y con clara vocación de servicio hacia clientes internos/externos. Contratación en relación de dependencia. Entorno de trabajo dinámico, flexible y orientado a objetivos.

http://www.computrabajo.com.ar/bt-ofrd-9727-57456.htm

Para Importante Proyecto se requiere analistas programadores con experiencia en JAVA, J2EE, C, y PERL. Se ofrece muy buena contratación, viajes, posibilidad de desarrollo. Se ofrece excelente contratación, clima laboral y desarrollo profesional.

http://www.computrabajo.com.ar/bt-ofrd-aclavel-77976.htm

We are looking for a *NIX perl programmer in Argentina for an off-site position - full time and part time positions are available. Required skills: Perl, OOPerl, DBI, CGI, apache. Desired skills: mod_perl, PHP, C/C++, DHTML/JS/DOM, Tangram, Catalyst.

Contact information: Send an email to busqueda@ank.com.ar - English or Spanish is OK. Need to be Argentine or hold a legal working visa for Argentina.

http://jobs.perl.org/job/2284

Se busca programador para importante proyecto a desarrollarse en Perl y PostgreSQL, no es indispensable saber los dos lenguajes pero si muy importante tener muy buen conocimiento de alguno de los dos debido a los tiempos de entrega de desarrollo.

El trabajo es para desarrollarse en nuestras oficinas y con alguna posibilidad de trabajo freelance dependiendo de conocimientos.

No omitir referencias y trabajos realizados a Martin Archanco (martin@nulit.com.ar)

http://www.computrabajo.com.ar/bt- ofrlistado.htm?Bqd=&Bqd=%2BTM031&Bqd=&Bqd=&BqdPalabras=perl

Mark Jason Dominus comenzó una lista para discutir su libro ``High Order Perl''. Aún sin tener el libro, de próxima publicación free, los comentarios que se hacen son muy agudos y siempre dejan algo para pensar y leer un poco más.

http://hop.perl.plover.com/

Perlcast es un podcast semanal que cubre varios temas relacionados con la programación Perl.

http://perlcast.com

Para todos los que por pasión, trabajo o por curiosidad, están en esta tarea de la programación, un site de noticias sobre el mundo del desarrollo del software

http://www.versioncero.com/

En resumidas cuentas, se acusa a los grandes como IBM, Sun y HP de usar al open source como una fábrica de software en lugar de empujarlos a seguir en la brecha de la generación de productos independientes de los comerciales.

http://www.zdnet.com.au/news/software/0,2000061733,39194786,00.htm

Para los que no lo conocen, Rob Pike es uno de los integrantes del mítico equipo que creo a Unix, y autor de los libros The Unix Programming Environment y The Practice of Programming se fue de Lucent (Bell Labs) hacia Google. Una nota de color.

http://www.nj.com/business/ledger/index.ssf?/base/business- 0/1117947410116141.xml&coll=1

Básicamente en ftp://ftp.cpan.org/pub/CPAN/src/

La conocida distribución de Perl de la empresa ActiveState ahora también está disponible para la última versión de Mac OS X (10.4 - Tiger)

http://www.activestate.com/Products/ActivePerl/OEM/

Como es costumbre los releases de Pugs (Perl 6 implementado en Haskell) son cada vez más frecuentes. Esta vez dos en un mes, siendo la versión 6.2.7 la más reciente.

http://search.cpan.org/~autrijus/Perl6-Pugs-6.2.7/

Este editor de XML para la documentación de Gentoo generado en Perl parece ser un clásico en esta distribución de Linux.

http://download.iansview.com/gendocedit/releases/gendocedit-0.40.tar.gz

La gente de Gentoo está trabajando en una distribución minimalista de Perl. Aún experimental pero con un éxito moderado, esta distribución ocupa 930KB frente a las 12.300 KB de la distribución completa.

Manejo de Excepciones (Mordiditas de aquí y de allá)

Por Víctor A. Rodríguez

Introducción

Otra entrega de Mordiditas de aquí y de allá, y esta vez el hincapié es sobre un tema que si bien es poco común y muchas veces no usado, nos va a hacer desaparecer una serie de problemas, mejorar la calidad del código, la estabilidad de nuestras aplicaciones y la posibilidad de poder ubicar rápidamente el código que está causando el problema en una falla. Entonces qué más podemos pedir ?? ... ah si claro, en esta oportunidad vamos a tratar el manejo de excepciones.

Como se está haciendo costumbre, la forma de desarrollar el artículo es primero presentar el problema junto con la solución, cuestión que puedas usarlo como si fuera una receta, después viene la parte de la explicación de cómo se llega a esta receta final, y es la parte más jugosa de todo el asunto. Ahora a sumergirnos en el manejo de excepciones ...

El problema y la receta

Básicamente cuando ejecutamos cualquier función de Perl esta devuelve datos y/o ejecuta alguna acción, y generalmente nos indica de alguna forma si la tarea falló o no y debido a qué problema. Normalmente esta forma es la devolución de un valor de error, el colocar el valor de error en un escalar (por ejemplo $Package::Error) y/o en el escalar especial $! que corresponde a errores del sistema operativo.

Hasta acá nada nuevo, pero veamos un pedazo de código que se usa para enviar un e-mail utilizando el módulo Net::SMTP. Tenemos que ver en cada operación si esta falló y tomar las acciones necesarias (actualmente es parte del código es el que utilizo para enviar esta publicación a la lista todos los meses) :

 sub sendMail($$$$$) {
        my $from = shift;
        my $to = shift;
        my $subject = shift;
        my $msg = shift;
        my $debug = shift;
 
        my $smtp = Net::SMTP->new( $SMTP_server, 
                                        Port=>$SMTP_port,
                                        Hello=>$thisMachine, 
                                        Debug => $debug );
        die "No puedo mandar e-mail" unless $smtp;
 
        $smtp->mail( $from ) || do {
                print "Error en clásula MAIL\n";
                return;
        };
        $smtp->to( $to ) || do {
                print "Error en clásula TO\n";
                return;
        };
 
        $smtp->data() || do {
                print "Error en clásula DATA\n";
                return;
        };
        $smtp->datasend("From: $from\n") || do {
                print "Error en clásula FROM\n";
                return;
        };
        $smtp->datasend("To: $to\n") || do {
                print "Error en clásula TO\n";
                return;
        };
        $smtp->datasend("Subject: $subject\n") || do {
                print "Error en clásula DATASEND\n";
                return;
        };
        $smtp->datasend("Content-Type: text/plain\n") || do {
                print "Error en clásula CONTENT-TYPE\n";
                return;
        };
        $smtp->datasend("\n") || do {
                print "Error en clásula HEADER-END\n";
                return;
        };
        foreach( @$msg ) {
                $smtp->datasend("$_") || do {
                print "Error en clásula MSG-BODY\n";
                return;
        };              
 
        $smtp->dataend() || do {
                print "Error en clásula DATAEND\n";
                return;
        };
 
        $smtp->quit || do {
                print "Error en clásula QUIT\n";
                return;
        };
 };

Ocurre que el código se empieza a hacer bastante confuso porque en cada llamada a una sub (o método) tenemos que verificar la condición de error. Si bien esto ayuda al fortalecimiento del código (mas bien es indispensable) también complica la comprensión del mismo debido a que ataca directamente a su linealidad.

Entonces si no hacemos un chequeo del código de error nuestro sistema se debilita y no funciona correctamente (damos por sentado que todo funcionó cuando puede no haber ocurrido así), en cambio si lo usamos tenemos problemas de legibilidad. Si nos ponemos a analizarlo, en realidad lo que nos molesta no es la detección misma de la condición de error, sino todo el código necesario para procesarla (en este caso no es abundante y ya resulta molesto). Una posible solución sería el poder discernir que parte es detección del error y cuál su tratamiento, pudiendo separar el código por tarea específica. Por ejemplo :

 use Error qw(:try);
 
 try {
        $smtp = Net::SMTP->new( $SMTP_server, 
                                        Port=>$SMTP_port,
                                        Hello=>$thisMachine, 
                                        Debug => $debug );
        throw Error::Simple( "No puedo mandar e-mail") unless $smtp;
 
        $smtp->mail( $from ) || 
                throw Error::Simple("Error en clásula MAIL");
        $smtp->to( $to ) ||
                throw Error::Simple("Error en clásula TO");
 
        ## Mucho código innecesario para el ejemplo
 
        $smtp->dataend() || 
                throw Error::Simple("Error en clásula DATAEND");
        $smtp->quit ||
                throw Error::Simple("Error en clásula QUIT");
 }
 catch Error::Simple with {
        my $error = shift;
 
        print "** $error->{-text}\n";
        $smtp = 0; ## destroy the object
        return;
 };

Casualmente el módulo Error.pm implementa esta funcionalidad, y permite usar excepciones de la siguiente forma : dentro del bloque try{} se coloca el código que devuelve errores, ya a sea a través de variables o usando un mecanismo para arrojar excepciones (sentencia throw{}), y en caso de suceder la ejecución del bloque try{} se detiene y comienza la ejecución del bloque catch{}. En nuestro ejemplo, si se produce un error al crear un nuevo objeto Net::SMTP se creará una excepción (del tipo Error::Simple) que hará que siga la ejecución a partir de la sentencia catch{}, pasando una referencia a un hash como primer parámetro, y el texto del error (indicado en cada sentencia throw) como la clave -text de dicho hash (para conocer otras claves del hash puede consultarse la documentación del módulo Error en CPAN).

Imagínense que si Net::SMTP también utilizara excepciones únicamente deberíamos llamar a los métodos correspondientes y la prueba de si hubo error se haría dentro del bloque catch{}, sin necesidad de incluirlo en el bloque try{}, resultando en un código completamente lineal (o al menos desprovisto de distracciones sobre el manejo de errores) :

 try {
        $smtp = Net::SMTP->new( $SMTP_server, 
                                        Port=>$SMTP_port,
                                        Hello=>$thisMachine, 
                                        Debug => $debug );
 
        $smtp->mail( $from );
        $smtp->to( $to );
 
        ## Mucho código innecesario para el ejemplo
 
        $smtp->dataend();
        $smtp->quit;
 }
 catch Error::Net::SMTP with {
        my $error = shift;
 
        print "** $error->{-text}\n";
        $smtp = 0;
        return;
};

Y colorín colorado, este cuento se ha terminado. O será que recién ha empezado ??

La explicación

Vamos a empezar como siempre: por el principio. Básicamente podemos definir que una excepción es una situación que no sucede sino en ciertas situaciones llamadas no comunes ( o sea, excepcionales :-) ). Y este es exactamente el caso de los errores, entonces si volvemos al código para envío de e-mails y hacemos un pequeño análisis estadístico vamos a ver que aproximadamente por cada línea de código destinada al envío de e-mails (situación normal) hay dos líneas dedicadas al manejo de errores (situación de excepción), o sea la mayor parte de nuestro código (un 66% aprox.) lo estamos usando en ocasiones excepcionales, y además dificulta la lectura del código que implementa las funcionalidades principales de nuestro programa : desde todo punto de vista un verdadero desastre. Sólo para agregar un insulto adicional, el código que maneja las excepciones es muy similar entre si, con lo que ni siquiera nos preocupamos por hacer reuso de código (convengamos que aquí sólo se trata sólo de una sentencia print, por ser este es un caso muy simplificado).

Empecemos a trabajar entonces. Si atacamos el reuso de código bien podría tratarse de encapsular el tratamiento de errores en una única sub que se llame doError() y que en nuestro caso sólo haría un print del texto pasado como primer y único parámetro :

 sub doError($) {
        my $msg = shift;
 
        print "** Error fatal : $msg\n";
 }

Ahora bien, si empezamos a investigar en los mecanismos que tiene Perl para el manejo de excepciones caemos en nuestros viejos amigos die() y eval(). En principio cuando se encuentra una situación en la que nuestro código encuentra un error este lo puede señalar a través de la ejecución de la sentencia die() pasando, opcionalmente, un string con la descripción del error. Por ejemplo cuando se genera una instancia de Net::SMTP (en nuestra sub sendMail) acto seguido se verifica que esta haya generado un valor en la variable $smtp y de no ser así se genera una excepción con la instrucción die ``No puedo mandar e-mail''. Si ejecutamos el script desde un shell y no podemos conectarnos al server de SMTP para enviar e-mails obtendremos el siguiente error :

 No puedo mandar e-mail at ./send.pl line 47.

Para hacer esto Perl utiliza un mecanismo de señales heredado de Unix. Básicamente las señales son eventos asíncronos que, en Perl, están accesibles a través del hash %SIG, y que pueden interceptarse asignando una referencia a un código al handler correspondiente a la señal. El mensaje generado anteriormente ocurre porque die() genera la señal __DIE__ que es interceptada por Perl mismo, y hace que se muestre el string pasado a la sentencia die() y termine la ejecución del script. Básicamente es como si Perl colocara un sentencia similar a la siguiente al principio de nuestro script para poder interceptar un die() :

 $SIG{__DIE__} = sub { print STDERR $_[0]; exit $!; };

Ajhá !! entonces hagamos nuestra propia intercepción de la señal __DIE__ y tomemos el control (``dame dame dame todo el power...''). No nos hagamos problemas, esto mas o menos ya lo hace el eval(). Mágicamente si envolvemos una serie de sentencias de nuestro código con la sentencia eval() esta va a realizar una tarea similar, colocando el error que ocurra en el escalar $@ :

 eval {
        $smtp = Net::SMTP->new( $SMTP_server, 
                                        Port=>$SMTP_port,
                                        Hello=>$thisMachine, 
                                        Debug => $debug );
        die "No puedo mandar e-mail" unless $smtp;
        $smtp->mail( $from ) || die "Error en clásula MAIL";
        $smtp->to( $to ) || die "Error en clásula TO";
 
        ## Muuuucho texto quitado por claridad ..
 
        $smtp->dataend() || die "Error en clásula DATAEND";
        $smtp->quit || die "Error en clásula QUIT";
 };
 if ($@) {
        doError($@);
        $smtp = 0; ## destroy the object
        return;
 };

Bueno bueno, sinceramente ahora si que está todo mucho más claro.

Llegado a este punto vamos a analizar en dónde estamos ubicados, ver si logramos el objetivo, si hay algún efecto colateral y según estos resultados veremos de seguir avanzando o no.

En principio logramos la reusabilidad de código con doError(), linealizamos nuestro código y centralizamos el manejo de las excepciones a través de las funciones die() y eval().

Ahora compliquemos un poco las cosas, y supongamos que queremos manejar más de una excepción, ya que no todos los errores indican lo mismo. Digamos que en nuestro ejemplo de enviar e-mails queremos que se tomen distintas acciones si hay un error al conectarnos al server (porque lo consideramos irrecuperable) o si lo hay dentro del protocolo SMTP porque lo consideramos recuperable. En este caso la forma de recuperar será esperar 10 segundos e intentar, como máximo 3 veces, el enviar el e-mail. Bueno, ahí vamos ...

 sub sendMail($$$$$) {
        my $from = shift;
        my $to = shift;
        my $subject = shift;
        my $msg = shift;
        my $debug = shift;
        my $smtp;
 
        for my $tries (1..3) {
                ## Try to execute ...
                eval {
                        $smtp = Net::SMTP->new( $SMTP_server, 
                                                        Port=>$SMTP_port,
                                                        Hello=>$thisMachine, 
                                                        Debug => $debug );
                        die "No puedo mandar e-mail" unless $smtp;
 
                        $smtp->mail( $from ) || die "Error en clásula MAIL";
                        $smtp->to( $to ) || die "Error en clásula TO";
                        $smtp->data() || die "Error en clásula DATA";
 
                        ## Muuuucho texto quitado por claridad ..
 
                        $smtp->dataend() || die "Error en clásula DATAEND";
                        $smtp->quit || die "Error en clásula QUIT";
                };
 
                ## Catch the error ...
                if ($@) {
                        $smtp = 0; ## destroy the object
                        if ( $@ =~ /Error/ ) {
                                doError ("Error recuperable ($tries)...");
                                sleep(10);
                                next;
                        };
                        if ( $@ =~ /No puedo/ ) {
                                doError("Error irrecuperable : $@");
                                return;
                        };
 
                        ## Otherwise return
                        return;
                };
        };
 };
 
 
 
Muy bien, no sólo hemos logrados nuestros objetivos sino que además podemos
aplicar distintos tratamientos según los errores que se nos presenten. Pero
(porque en definitiva uno nunca está satisfecho) nos queda algo pendiente, y
es que si las subs/métodos no hacen uso de die() entonces tenemos que
agregar la detección del error y convertirla en una excepción. En nuestro
ejemplo de envío de e-mails pasa exactamente eso (todos los métodos de
Net::SMTP indican que hubo un error devolviendo undef ) , entonces debemos
verificarlo y en tal caso generar la excepción en cada una de las
invocaciones.

En nuestro caso es relativamente simple, pero en grandes cantidades de código puede convertirse en un problema, entonces ... por qué no solucionarlo ??

Como siempre viene en nuestra ayuda el amigo CPAN, y por ahí descubrimos un modulito que se llama Fatal y que permite exactamente hacer lo que necesitamos : en caso que una serie de subs/métodos devuelvan un valor falso como indicador de error este módulo los detecta y genera una excepción que puede ser capturada con eval(). Basta con que al poner la sentencia use le indiquemos qué módulos debe monitorear y quitar las verificaciones y sentencias die() luego de cada una de los llamadas a los métodos de Net::SMTP ... entonces manos a la obra :

 use Fatal qw(Net::SMTP::new Net::SMTP::mail Net::SMTP::to
                ## ... y el resto de los módulos ...
                Net::SMTP::quit);
 
 
Al ejecutar el código se genera la excepción como esperamos pero ya no
detecta si el error se puede recuperar o no, y actuar en consecuencia, sino
que ante un error termina la ejecución de sendMail sin mas. De vuelta al
tablero de debugging vemos que en realidad al detectar la secuencia de error
(comentario Catch the error ...) no reconoce ninguno de los dos strings
"Error" o "No puedo" y la próxima sentencia que ejecuta es el return que
sigue a los dos if. Esto ocurre básicamente debido a que usamos el mensaje
de la excepción (almacenado en $@) para detectar que tipo de error se
trataba y al delegarlo en el módulo Fatal estos han cambiado. Como ejercicio
vamos a cambiar el bloque de manejo de errores por el siguiente :
        ## Catch the error ...
        if ($@) {
                $smtp = 0; ## destroy the object
                if ( $@ =~ /Error/ ) {
                        doError ("Error recuperable ($tries)...");
                        sleep(10);
                        next;
                };
                if ( $@ =~ /No puedo/ ) {
                        doError("Error irrecuperable : $@");
                        return;
                };
 
                ## Otherwise return
                doError( "Error no reconocido : \n $@");
                return;
        };

Si volvemos a ejecutar nos encontramos con la siguiente salida de la ejecución :

 ** Error fatal: Error no reconocido :
 Can't to(Net::SMTP=GLOB(0x80fb38), victor@bit-manxxxxxxxxxxx.com.ar), $! is "" at (eval 5) line 3
 main::__ANON__('Net::SMTP=GLOB(0x80fb38)','victor@bit-manxxxxxxxxxxx.com.ar') called at send.pl line 68
 eval {...} called at send.pl line 60
 main::sendMail('"V\x{ed}ctor A. Rodr\x{ed}guez" <victor@bit-man.com.ar>','victor@bit-manxxxxxxxxxxx.com.ar','TEST !!!','ARRAY(0x8741cc)',1) called at send.pl line 107

que no es sencillamente, nada mas ni nada menos, que el mensaje que coloca el módulo Fatal en la variable $@. Si miramos un poco vamos a ver que este contiene la sub que provocó el error, con qué argumentos fue llamada y la lista de sub que la llamaron (algo así como un stack trace). Evidentemente tratar de sacar algo de información este texto para ver si es un error recuperable, no recuperable o lo que sea se hace un tanto problemático (por no decir imposible, no recomendable ni apto para la salud mental propia o ajena).

Oh, y ahora quién podrá defenderme ... pero nadie viene al rescate, así que no queda otra que seguir trabajando si queremos conseguir nuestro objetivo extra de linealizar completamente el código y mantener el manejo de excepciones en forma separada. Como de costumbre hay una serie de módulos que nos permiten hacerlo, y podemos decir que son generalizaciones del módulo Fatal que nos dejan no sólo actuar acorde al valor devuelto según sea verdadero o falso, sino algo más genérico : interceptar la llamada a una función y colocar un wrapper (envoltorio) alrededor de la misma de tal forma de ejecutar código antes y después de su ejecución. Así podemos validar el código de ejecución de cada sub o método, una vez ejecutado, y realizar la excepción a través de un die() en caso que sea necesario ... y todo fuera de nuestro flujo normal !!!

Este tipo de módulo se encuentran bajo el namespace Hook, y un muy buen ejemplo es Hook::Lexwrap (otros módulos que cumplen una función similar son Class::Hook , Hook::Scope, Hook::Heckle, Hook::WrapSub y Hook::PrePostCall ). Su uso básico es decir a que sub o modos se desea hacer un wrapper , cuándo (antes o después de su ejecución) y qué código ejecutar pasándole una referencia al mismo código. Por ejemplo, si queremos generar una excepción cuando new() no devuelve una referencia porque la creación del objeto falló entonces lo especificamos como un wrapping del método new() correspondiente al módulo Net::SMTP (específicamente Net::SMTP::new) pero sólo una vez que esta ha sido ejecutada (post) :

 use Hook::LexWrap;
 
 wrap Net::SMTP::new,
        post => sub { 
                        die("No puedo mandar e-mail")
                                if ! defined $_[-1];
                };

Un breve detalle de implementación : a los argumentos recibidos (array @_ ) se agrega un elemento uno adicional en el último lugar, y es el valor de vuelto por el sub o método. Este puede accederse como el elemento -1 (primer elemento desde el final).

Hasta aquí los objetivos propuestos fueron logrados y seguramente hay muchos otros métodos más, con sus tantas y correspondientes ventajas y desventajas, que haría imposible la tarea de tratar de terminar de escribir este artículo alguna vez.

Adicionalmente hay un pequeño problema, y es con la implementación de la excepción a través del mecanismo de eval()/die(). Esta tiene dos puntos débiles :

Estos dos efectos, más el segundo que le primero, los pudimos sufrir y ver en el transcurso de este artículo.

Una vez más si miramos en CPAN vamos a encontrar que alguien ya tuvo este problema y lo solucionó de una forma muy grácil y elegante. El módulo en cuestión se llama Error, y básicamente hace el mismo trabajo que estuvimos viendo con eval() y die() pero encapsulado y con exposición de las excepciones a través de un mecanismo Object Oriented (OO - Orientado a Objetos) ... sin miedos, que solamente vamos a usar algo simple sin necesidad de conocer qué son y cómo se especifican o se codifican los objetos ( prometo que, si les interesa, en próximas ediciones de CaFe Perl va a aparecer un artículo sobre orientación a objetos y el uso específico con el manejo de excepciones ).

Simplemente este módulo nos permite ejecutar el código que puede devolver errores dentro de una sentencia try(), manejar las excepciones en uno o más bloques catch() y si ocurre una excepción no especificada atraparla con otherwise() ... casualmente como en nuestro código ;-)

Asimismo cuando detectamos un error, para convertirlo en una excepción lo hacemos a través de la función throw() usando una excepción del tipo Error::Simple (objeto usado para arrojar excepciones y cuya clase está definida en Error.pm ) aceptando el texto del error como único argumento :

 use Error qw(:try);
 
 sub sendMail($$$$$) {
        my $from = shift;
        my $to = shift;
        my $subject = shift;
        my $msg = shift;
        my $debug = shift;
        my $smtp;
 
        ## Try to execute ...
        try {
                $smtp = Net::SMTP->new( $SMTP_server, 
                                                Port=>$SMTP_port,
                                                Hello=>$thisMachine, 
                                                Debug => $debug );
 
                throw Error::Simple( "No puedo mandar e-mail" )
                        unless $smtp;
 
                $smtp->mail( $from ) 
                        || throw Error::Simple( "Error en clásula MAIL" );
                $smtp->to( $to ) 
                        || throw Error::Simple("Error en clásula TO");
 
                ## Muuuucho texto quitado por claridad ..
 
                $smtp->quit 
                        || throw Error::Simple( "Error en clásula QUIT" );
        }
 
        ## Catch the error ...
        catch Error::Simple with {
                my $error = shift; ## como primer argumento un hash con
                                         ## data sobre el error.
 
                doError( $error->{-text} );
                $smtp = 0; ## destroy the object
                return;
        }
 
        otherwise {
                doError("Untrapped error");
                $smtp = 0; ## destroy the object
        };
 
        return;
 };
 
 
Como ven no hay nada nuevo, simplemente que la implementación es más limpia
... aunque aún tenemos el problema de tener que verificar y arrojar la
excepción con throw(), también podemos mezclarla junto con los módulos Fatal
o Hook::Lexwrap y obtener un manejo de las excepciones mejorado, como lo
hicimos antes usando eval() y die().

Una mirada más allá

No cabe duda que ir del manejo de errores al de excepciones es un gran paso en la mejora de nuestra capacidad de resolución de problemas. Hay un paso adicional, aunque seguramente habrá muchos más, y es el de exception-safety (excepciones seguras); este tiene que ver ya no con la linealidad de la codificación sino con la consistencia de los datos. Podríamos definir que un componente queda en un estado seguro, o consistente, cuando luego de producirse un error este no presenta efectos secundarios tales como cambio en sus especificaciones, comportamiento, o problemas como el consumo contínuo de memoria (memory leaks) y el no cerrado de file handlers. Para el caso de exception-safety también se especifica qué nivel de seguridad está soportado en el módulo o componente, pudiendo ir desde el ``no compromiso'' (no hay seguridad de que el comportamiento siga siendo el mismo en caso de error) hasta el de ``transparencia a las fallas'' donde se asegura que no importa lo que ocurra la operación se llevará a cabo.

Otro punto importante a destacar, de cara al futuro, es que el mecanismo de manejo de excepciones en Perl 6 está modelado según el módulo Error que se ve al final de este artículo, y en las siguientes RFCs de Perl 6 pueden ver más información sobre este tópico :

Que tenga una programación excepcional. Nos vemos la próxima !!!

Infografía

POC (peace of code)

por Matías Palomec (matias@cafelug.org.ar)

La idea de este script es poder mostrar cuál debe ser el contenido de la variable DISPLAY para poder usar X-Window en forma remota.

Originalmente este script eran dos o tres líneas, parseaba el lastlog buscando la primer aparición del usuario UNIX. Luego se lo dí a una persona que también tiene problemas para encontrar la IP de la máquina que está utilizando, y tuve que ir modificando algunas cosas (usar el $ENV{'USER'} en vez del usuario a secas).

La utilidad que tiene es bastante simple, comienza a leer el lastlog, buscando el TTY del shell en donde se ejecuta el script, y también el usuario. Cuando lo encuentra, saca la IP (ya sea que de la IP de una o que tenga que resolverla con el dig). Y al final muestra por pantalla la IP en la forma:

 export DISPLAY=10.10.10.10

Esto es más que nada para que ya me quede la línea armada para ponerla en algún server y levantar el display remoto sin problemas. Al no usar módulos, y los programas externos que uso son muy usuales, es bastante portable (salvo por el dig, pero es posible el reemplazarlo por el nslookup).

 #!/usr/bin/perl -w
 
 my $flag=0;
 my $tty=`/usr/bin/tty`;my $dig="/usr/bin/dig";
 my $domain="dominio";
 $tty=~s/(\n|\r|\r\n)$//;
 $tty=~s|/dev/||;
 my $ip = "";
 chop($tty);
 my $tflag = 1;
 open("TMP","last|") || die "No pude ejecutar last: $!";
 while (($tflag) && ($line = <TMP>)){
        if (($line =~ m/$ENV{'LOGNAME'}/) && ($line =~ m/$tty/)
                && ($line =~ m/still/i)){
                my @li = split(" ",$line);
                $ip = $li[2];
                close(TMP);
                $tflag = 0;
        }
 }
 if(($ip=~ m/(\d{1,3})?\.(\d{1,3})?\.(\d{1,3})?\.(\d{1,3})?/)
        &&($1<255)&&($2<255)&&($3< 255)&&($4<255)){
        $ip="$1.$2.$3.$4";
        $flag=1;
 }else{
        @b=split(" ",$ip);
        $b[2]=~ s/([a-b]*)\..*/$1/;
        $b[2]=`$dig $b[2].dominio|grep $b[2]|grep -v "^;"|awk '{print \$5}'`;
        $ip=$b[2];
        $ip=~s/(\n|\r|\r|n)$//;
        $flag=1;
 }
 print"export DISPLAY=$ip:0\n" if($flag);
__END__