archivo de word->txt->perlazo->html

Este es un post sobre el fino arte de piratearse un ebook, aunque no voy a decir que me incrimine, excepto que se trata de una autora contemporanea, de la que hace poco comenté aquí mismo que leí cierta saga de 4 libros sobre vampiritos amables.

Bueno, la historia está así, tengo un archivo de word (.doc), entonces, lo primero que hice fue convertirlo a txt en el word (duh!), luego con el script que está abajo que además usa el programa “txt2html” se divide el texto en capítulos y se convierte a HTML para ser subido a mi iphone y pueda ser leído con el BookReader.

Si tuve que hacerle algunos cambios al “txt”, porque el script no es muy inteligente (como su dueño) (no es mío !!), pero nada que tome más de 1 minuto, al final tengo en mi iphone listo para leerse la más reciente novela de la susodicha misteriosa.

[codesyntax lang=’perl’ container=’pre’]#!/opt/local/bin/perl -w
use strict;
use warnings;

my $file = $ARGV[0];
open FILE, “< $file"; my $oldname = "CHAPTER 0"; open FILE_OUT, ">$oldname.txt”;
while () {
chop;
my $linea = $_;
if ( $_ =~ m/^CHAPTER [\d]+$/ ) {
print “$_ – “;
my $chap = $_;
my $name =
;
chop $name;
print “$name\n”;
close FILE_OUT;
open FILE_OUT, “>$chap-$name.txt”;
`txt2html “$oldname.txt” > “$oldname.html”`;
$oldname = “$chap-$name”;
$linea = “$chap – $name”;
} elsif ( $_ =~ m/^Page [\d]+$/ ) {
$linea =
;
$linea =
or $linea = “”;
}
print FILE_OUT “$linea\n” or warn “Fin de archivo?”;
}
`txt2html “$oldname.txt” > “$oldname.html”`;
[/codesyntax]

El documento original usa un par de patrones para los títulos de cada capítulo y para las páginas, entonces de eso me aproveché para poder dividir en archivos cada capítulo, y bueno, eliminar la molesta etiqueta de “Page 1… Page 2…”. Con lo cual doy el primer paso para dedicarme a convertidor de textos a ebooks 😛

SPOILER: El libro es “The host” o “La Huesped” creo se llama en español, de Stephenie Meyer de quien me declaro fans ! (en realidad esto es pa ayudarle a google a indexar el post 🙂

CGI::Aplication el fino arte de hacer web perl-style

Perl es una maravilla en cuanto a robustes y disponibilidad de módulos para hacer lo que sea, no solo es posible analizar logs al más puro estilo geek, sino que se pueden hacer cosas como “parsear” un archivo de excel para importarlo a una base de datos, o crear sitios web con todas las características que necesitan.

Para crear aplicaciones Web, Perl tiene un monton de opciones, desde hacer un CGI a mano que haga toda la magia de entender los headers, extraer los argumentos de GET y POST, y desplegar HTML intercalado en el código, al más puro estilo de PHP (porque lo heredó de Perl), sino que existen muchos módulos (CPAN) que nos ayudan a hacer las cosas de manera más ordenada y mantenible.

En esta nota quiero abordar la utilización de un módulo muy robusto, casi podríamos decir que es realmente un Framework de desarrollo web, aunque a mi no me gustan los frameworks,  uno no deja de admirar la simplicidad con la que se puede crear una simple paginita web siguiendo bastante fielmente algunos de las más famosas “mejores prácticas”, como el MVC, el uso de capa de abstraccion en la base de datos, y sobre todo el crear código seguro contra ataques de injeccion SQL y XSS.

Para empezar vamos a plantear el clásico ejemplo de crear un blog muy sencillo, que tenga autenticación, guarde en mysql, y nos permita mandar noticias y poner comentarios en ellas, nada exótico.

Empezemos por crear una base de datos:
[codesyntax lang=’sql’ container=’div’]mysql> create database perlblog;
Query OK, 1 row affected (0.09 sec)

mysql> grant all privileges on perlblog.* to [email protected] identified by `20y1337` ;
Query OK, 0 rows affected (0.27 sec)
mysql>[/codesyntax]
Necesitamos varias columnas, una para los datos del usuario (user), otra para las noticias (news), y otra para comentarios (comments), luego veremos si se requiere algo extra.
[codesyntax lang=’sql’ container=’div’]mysql> create table user (id int unsigned not null auto_increment, login varchar(50) unique, password char(32), name varchar(150), created datetime, last_log datetime, primary key (id));
Query OK, 0 rows affected (0.21 sec)

mysql> create table news (id int unsigned not null auto_increment, title varchar(254), summary tinytext , news text, posted datetime, last_edit datetime, user_id int unsigned not null default 1, primary key (id));
Query OK, 0 rows affected (0.43 sec)

mysql> create table comment (id int unsigned not null auto_increment, title varchar(254), comment text, posted datetime, last_edit datetime, user_id int unsigned not null default 0, name varchar(255), email varchar(255), primary key (id));
Query OK, 0 rows affected (0.09 sec)
[/codesyntax]

El la columna del usuario al menos nos hace falta su correo electrónico, agreguémoslo:
[codesyntax lang=’sql’ container=’div’]mysql> alter table user add column email varchar(255) after password;
Query OK, 0 rows affected (0.18 sec)
Records: 0  Duplicates: 0  Warnings: 0
[/codesyntax]

Ya estamos listos para empezar, posiblemente lo primero que queremos hacer es poder insertar una noticia, así que vamos a crear una paginita de captura. Para esto necesitamos crear el ambiente inicial:

[codesyntax lang=’perl’ container=’pre’]package Blog;
use strict;
use warnings;
use base ‘CGI::Application’;
use CGI::Application::Plugin::AutoRunmode;
use CGI::Application::Plugin::DBH (qw/dbh_config dbh/);

sub cgiapp_init {
my $self = shift;
$self->dbh_config(“dbi:mysql:perlblog”, “blog”, `20y1337`);
}
sub news_list : StartRunmode {
my $self = shift;
my $q = $self->query();

my $sth = $self->dbh->prepare(“SELECT * FROM news ORDER BY posted DESC”);
$sth->execute();
my @rows;
while ( my $row = $sth->fetchrow_hashref ) {
push(@rows, $row);
}
return @rows;
}
1;
[/codesyntax]

Con esto estamos armando el ambiente más mínimo posible, estamos creando un paquete que va a contener todas las rutinas que requiere la aplicación, y estamos usando uso de CGI::Application que será nuestra base para hacer prácticamente toda la programación.

La subrutina “cgiapp_init” es la encargada de inicializar todos los requerimientos, que por lo pronto solamente es la conección a la base de datos, para lo cual estamos usando un plugin para CGI::Application que se llama CGI::Application::Plugin::DBH, en síntesis es meterle la capa DBI directamente nuestro “framework”.

Para poder ejecutar lo que llevamos de aplicación tenemos que crear un script que ejecuta el paquete, lo cual es bastante sencillo, y no volveremos a tocarlo una vez escrito lo siguiente a “index.pl”:

[codesyntax lang=’perl’]#!/opt/local/bin/perl -w
use strict;
use warnings;
use Blog;
my $app = Blog->new();
$app->run();
[/codesyntax]

Noten que estoy usando un path para perl medio extraño, el 99% de los casos se usará el estandar “#!/usr/bin/perl -w”, yo uso ese pq me gusta más trabajar con el perl instalado por macports, el perl nativo de Mac OS X está medio chafita en algunos aspectos y prefiero algo más estandar, con suerte casi nadie que lea este post va a desarrollar sobre Mac 🙂

Una vez creados estos 2 archivos en el directorio de los “cgi-bin”, entonces visitamos en el navegador algo como http://localhost/cgi-bin/index.pl y vamos a ver un número entero, posiblemente cero, que será el número de renglones que existan en la columna “news” de la base de datos.

Con esto estamos aprendiendo un truco útil cuando estemos usando CGI::Application, siempre que quieras debugear algo, puedes poner un return de la variable que sea interesante, funciona como el típico “print” para debuguear, la diferencia es que el return termina en ese punto la ejecución de la rutina, y nos devuelve a la pantalla del navegador solamente el valor de dicha variable. Podríamos haber puesto “return ‘hola mundo'”, pero no sería tan divertido.
Ahora insertemos un renglon en “news”, ejecutando:

[codesyntax lang=’sql’ container=’div’]insert into news values (null, ‘titulo 1’, ‘estes es un resumen’, ‘y esta es la noticia extendida’, now(), now(), 1);
[/codesyntax]

Si recargamos la página en el navegador veremos un “1”, nada excitante, pero podemos meterle más emoción retornando “$rows[0]{‘title’}” en vez de “@rows”, con lo cual ahora veremos un excitante “titulo 1” !!! solo estamos demostrandonos a nosotros mismos que “podemos interactuar con una base de datos de verdad”.

Ahora vamos al paso de presentar esa información en un formato decenton, vamos a crear una plantilla en HTML muy sencilla, solo para llenar el requisito, una vez entendido todo el proceso, será trivial ir a buscar un diseño gratuito y adaptarlo.

[codesyntax lang=’html4strict’]<?xml version=”1.0″ encoding=”UTF-8″?>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.1//EN”
“http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” xml:lang=”en” >
<head>
<title>[% title %]</title>
</head>
<body>
<p>[% title %]<</p>
</body>
</html>
[/codesyntax]

Tenemos un documento básico de XHTML, pero noten que el título tiene algo extraño ([% title %]), es la presentación del contenido de una variable (title) en el “lenguaje” de Template Toolkit (TT), que será lo que utilizaremos para las plantillas.

El código de nuestro paquete necesita unos cambios ligeros, necesitamos inicializar el sistema de plantillas, y necesitamos decirle a “news_list” que interprete la plantilla “index.html”, vamos a agregar a la rutina “cgiapp_init” esto:

[codesyntax lang=’perl’ container=’div’]$self->tt_config(
TEMPLATE_OPTIONS => {
INCLUDE_PATH => ‘/Users/max/templates/’,
},
);
[/codesyntax]

Y en “news_list” vamos a sustituir la última linea, la del return, por esto:

[codesyntax lang=’perl’ container=’div’]my $vars = {
title => ‘Soy el blog m&aacute;s 1337’,
rows => \@rows,
};
#return $rows[0]{‘title’};
return $self->tt_process(‘index.html’, $vars);
[/codesyntax]

Con esto estamos mandandole a la plantilla 2 variables, aunque solo está utilizando una de ellas. Puedes darle “recargar”, “reload”, o como diga tu navegador :).

Ahora vamos a usar la segunda variable, que en realidad es una ·$&%$&/· referencia a un arreglo que tiene por elementos hashes (o arreglos asociativos), pa su madre no ?? como nota personal, odio las referencias en perl, siempre me confundo en que es una referencia y que no, pero bueno, sigamos.

Queremos cliclar sobre las noticias, pero solo hemos insertado una, inserta unas 2 o 3 más, incluso pueden usar exactamente los mismos datos que la primera, pero a veces ayuda tener una pista de que algo diferente se está haciendo, puedes cambiar el 1 por 2 y luego 3 y luego 4 en el título, y ya con eso tenemos.

Lo que haremos en la plantilla es agregar un ciclo sobre el arreglo “@rows”, no es nada complicado, queda más o menos así:

[codesyntax lang=’html4strict’]

…..

<p>[% title %]</p>
[% FOREACH row IN rows %]
<p>[% row.title %] ([% row.posted %])</p>
<p>[% row.summary %]</p>
<p><a href=”index.pl?rm=view&id=[% row.id%]”>Ver noticia</a></p>

….

[/codesyntax]

Con esto ya tenemos el listado de las noticias, ahora falta que podamos ver la noticia en extenso, y agregarle comentarios. Con el siguiente paso vamos a aprender como se definen más actividades a través de un solo módulo.

En el último cambio a la plantilla estamos agregando una liga, la cual contiene 2 variables con sus respectivos valores, “rm”, e “id”, el primero contiene el nombre de la rutina que se desea ejecutar en el módulo “Blog”, en este caso es “view”, y la segunda variable es el dato extra que necesitamos para saber cual noticia desean ver.

Lo que necesitamos ahora es tener la rutina “view”, donde vamos a seleccionar de la base de datos la noticia con el “id” que el usuario desea ver, y la vamos a mostrar en su respectiva plantilla.

La vista es sencilla, solamente tenemos que seleccionar el ID de la noticia que se desea ver, y mandarla a la plantilla:

[codesyntax lang=’perl’ container=’pre’]sub view : Runmode {
my $self = shift;
my $q = $self->query();
my $sth = $self->dbh->prepare(“SELECT * FROM news where id = ?”);
$sth->execute($q->param(‘id’));
my $row = $sth->fetchrow_hashref;
my $vars = {
title => $row->{‘title’},
row => $row,
};
return $self->tt_process(‘view.html’, $vars);
}
[/codesyntax]
Podemos ver que la vista se define solamente con “Runmode”, esto quiere decir que este módulo solo se va a ejecutar cuando sea llamado explícitamente (rm=view en GET). Y la plantilla puede ser tan sencilla como esto:
[codesyntax lang=’html4strict’]<p>[% title %]</p>
<div>
<h1>[% row.title|html %] ([% row.posted|html %])</h1>
<h2>[% row.summary|html %]</h2>
<div>[% row.news|html %]</div>
<p><a href=”index.pl”>Ir al Inicio</a></p>
</div>
[/codesyntax]
Con esto tenemos la base de la aplicación, pero solo visualmente, todavía no podemos mandar comentarios, ni mandar noticias.
Lo que necesitamos hacer es crear una forma en la plantilla, y recibir en una vista lo que se envía por POST. El modulo “view” cambia un poco, queda mas o menos como esto:
[codesyntax lang=”perl” container=’pre’]sub view : Runmode {
my $self = shift;
my $sth;
my $q = $self->query();
if ( $q->param(‘submit’) ) { # POST METHOD, let’s save the comment
$sth = $self->dbh->prepare(“INSERT INTO comment (title, comment, posted, last_edit, user_id, name, em
ail, news_id) VALUES ( ?, ?, now(), now(), 0, ?, ?, ?)”);
$sth->execute( $q->param(‘title’), $q->param(‘comment’), $q->param(‘name’), $q->param(’email’), $q->param(‘id’)
);
}
$sth = $self->dbh->prepare(“SELECT * FROM news WHERE id = ?”);
$sth->execute($q->param(‘id’));
my $row = $sth->fetchrow_hashref;
$sth = $self->dbh->prepare(“SELECT * FROM comment WHERE news_id = ? ORDER BY posted DESC”);
$sth->execute($q->param(‘id’));
my @comms;
while ( my $comm = $sth->fetchrow_hashref ) {
push(@comms, $comm);
}
my $num_c = @comms;
my $vars = {
title => $row->{‘title’},
num_c => $num_c,
row => $row,
comms => \@comms,
};
return $self->tt_process(‘view.html’, $vars);
}
[/codesyntax]
Con esto está casi completo el objetivo de este pequeño tutorial, pero falta una parte importante, la autenticación, pero como estamos haciendo todo al menor estilo de CGI::Application, solo tenemos que agregar unas cuantas lineas:
[codesyntax lang=’perl’ container=’pre’]use CGI::Application::Plugin::Session;
use CGI::Application::Plugin::Authentication;
Blog->authen->config(
DRIVER => [
‘DBI’,
TABLE => ‘user’, CONSTRAINTS => {
‘user.login’ => ‘__CREDENTIAL_1__’,
‘MD5:user.password’ => ‘__CREDENTIAL_2__’,
},
],
LOGOUT_URL => ‘http://latin.example.com/cgi-bin/index.pl’,
STORE => ‘Session’,
);
Blog->authen->protected_runmodes(qw(view));
[/codesyntax]
y un hacer un INSERT en la tabla “user”:
[codesyntax lang=’sql’]insert into user values (null, ‘admin’, MD5(‘admin’), ’[email protected]’, ‘Admin’, now(), now());
[/codesyntax]
El módulo de autenticación tiene la capacidad de reusar la instancia de DBI, y además nos crea una página de logueo, e incluso redirecciona “inteligentemente” a la página que estabamos tratando de accesar. La información que ponemos en su inicialización es la estrictamente necesaria, la tabla donde están las columnas de usuario y clave, las columnas, el método de “encriptación”, y por supuesto el método que se usará para autenticar, no tiene que ser una base de datos escrictamente, se puede crear un driver personalizado para soportar cosas más exóticas.

Y finalmente un punto importante que debemos notar, el uso de sesión, es necesario que guardemos en sesión los datos de autenticación, o el usuario final va a tener que estar metiendo su nombre usuario y clave con demasiada frecuencia, con solo agregar el módulo de session, y decirle al driver de autenticación que guarde las variables en él, ya tenemos un método de autenticación completo y funcional.

Noten que solo estoy poniendo como protegido al módulo “view”, podemos poner a cualquier módulo protegido, (ej. qw(view list otro_modulo) pero normalmente la lista de noticias nos gusta mantenerla pública, y de hecho normalmente la vista extendida de la noticia también, pero en este caso tenemos en el mísmo módulo tanto la vista extensa, como el agregar comentarios, es una buena idea separar los comentarios en otro módulo, de tal manera que podamos atomizar mejor los privilegios sobre la página.

Obviamente este ejemplo es muy mínimo, y se ve terriblemente mal, porque no tiene ni siquiera CSS, pero todo eso se corrige con la ayuda de un diseñador, los programadores no somos buenos diseñadores, así que pa que le hacemos al cuento? (tip: baja una plantilla gratuita de algun sitio 🙂

Espero que a alguien le parezca interesante este resumen de cómo usar CGI::Application, la documentación es bastante buena, pero cada módulo está documentado independientemente, y a veces es un poco complicado para alguien (como yo) de encontrarle la hilación, pero una vez que se entiende la base, lo demás es muy sencillo.
Ah !!, y una de las grandes ventajas de usar CGI::Application es que fácilmente se puede montar tanto sobre CGI como en mod_perl, aunque nunca he usado mod_perl :), pero en teoría no hay prácticamente nada que hacer para que funcione en ambos.

Eso es todo amigos, buen día 🙂

chrome OS: al fin se hace realidad(?)

La noticia de hoy es la propuesta de google a las necesidades modernas de sistemas operativos.

En realidad la propuesta es poco ambiciosa, ya que se enfocará a las “netbook”, las laptops pequeñitas que se han puesto de moda de un año para acá. Y bueno, de las palabras oficiales se entiende que trataran de generar un sistema operativo bastante mínimo, al menos al principio (espero), que trabaje prácticamente todo sobre web, aprovechando

Entre lo más relevante que veo de la propuesta, creo que intentarán replicar el comportamiento de las Mac en cuanto al hibernado/suspención, de tal manera que no tenga nadie que reiniciar más de una o dos veces por mes, o menos, tal vez solo para actualizar.

Lo bueno de la noticia en general es que escogieron Linux, así que podemos esperar un repunte de su popularidad para el año que viene, seguramente no van a lograr algo espectacular, al menos no en un año, pero esto podría significar mucho para Linux a mediano y largo plazo y claro para el SL en general, aunque el SL como tal ya es mucho más popular en sí que Linux.

Para antes de que termine este año veremos a un monton de geeks empezar a probar el nuevo sistema operativo :), me incluyo en la lista.

el recuento de los daños

No puedo dejar pasar la oportunidad para clausurar con broche de oro el final de las elecciones, poniendo esta bella foto como continuación del post anterior

837

Creo que queda claro de que se trató la campaña, de hacerse weyes poniendo espejitos para incandilar a la gente, cuanto apuestan a que cada “contenedor de basura” costó varios miles de pesos ?? pero no les alcanzó para mandar a recojerlos con todo y la basura que tenían, la tuvieron que dejar ahí !! 🙂

Que bueno que ya se acabó la campaña !!, al menos ahora los robos no van a ser tan evidentes.

Problemas para respaldar Iphone 3.0

Desde ayer o antier empezó a darme un error la sincronización del iphone, el itunes me decía algo como:

iTunes could not backup the iPhone “GaReGeD” because the iPhone disconnected.”

Estuve buscando en google soluciones y encontré que este problema se ha dado en diferentes circumstancias desde el firmware 2.x.x, en general está relacionado con el jailbreak, pero ninguna de las soluciones se ajustaba a mi version del OS, a pesar de que la “sintomatología” era la misma, se sincroniza música, aplicaciones y datos correctamente, pero no se respalda el iphone.

Pues finalmente logré crear un respaldo borrando TODAS las aplicaciones, intenté borrando unas cuantas primero, pero nunca se corrigió el problema, entonces lo que hice fue conectar el iphone, seleccionarlo en el itunes, irme a la pestaña de aplicaciones y deseleccionar la opción de sincronizar las aplicaciones, luego dar click en “sync”, esperar a que eliminara todas las aplicaciones, obviamente se pierden los datos, pero no me preocupa mucho, puedo volver a jugar desde el principio los juegos :), una vez que termina de eliminar todas las aplicaciones le di click derecho (dos deditos si es una macbook) en el icono del iphone y seleccione “backup”, con eso creo el respaldo de hoy, así que si llego a tener mayores problemas al menos se que puedo volver a lo que tenía hoy 🙂

Una vez que se termina el respaldo, toma 5-10 minutos, puedes instalar de nuevo todas las aplicaciones, y el sincronizado sigue funcionando 🙂

Estoy casi seguro que el problema tiene que ver con usar installo.us con sincronización hacia el itunes, pero no lo he comprobado.

Los valores de las campañas electorales

Aprovechando el momento, hay que aprovechar para criticar aprovechadamente a los aprovechados políticos

Ya desde hace unas semanas vi en Cuernavaca una bonita campaña en la que ponen unos “depósitos de basura” bastante ridículos, consistentes en un aro pegado a un poste, y sobre el aro una bolsita de plástico.

Cual fue la primera impresión ? que babosada ! cuanto van a durar las bolsitas ?? a menos de que las recojan diariamente.

Pues el resultado fué el esperado, nunca las recojen !!, así que ahora podemos ver, justo en visperas de las elecciones, una bonita imagen que nos anima a votar por las grandes ideas de este candidato y su partido.

834

No está demás decir que ese es uno de los que en mejores condiciones está, justo del otro lado de la calle está otro ya sin bolsa, y la basura está repartida a unos 20 metros a la redonda sobre la banqueta.

Que otras ideas tienes Alejandro Mojica ?