Google Refine + Perl
Google Refine é show. Para quem não conhece, acesse já o site oficial e assista pelo menos o primeiro vídeo. Serve para inúmeras tarefas envolvendo ETL.
Eu, no momento, uso muito para coisas simples, como carregar um CSV, eliminar outliers e salvar em JSON para carregar no MongoDB. Nada que um one-liner em Perl não pudesse fazer.
Infelizmente, a recíproca não é válida: one-liners em Perl são bem mais versáteis do que o Google Refine. Que tal integrar os dois?
- Google Refine pode ser facilmente integrado com um web-service qualquer.
- Perl transforma one-liners em webservices.
- PROFIT!!!
Como exemplo concreto, utilizarei dados georeferenciados. Digamos que preciso eliminar registros duplicatas, e uma das formas de detectar é pela proximidade física dos pontos no mapa. Google Refine não é um GIS, e não faz a menor ideia de como processar latitude/longitude. Entra o GeoDNA: um algoritmo que transforma coordenadas numéricas bidimensionais em uma string com propriedade interessante: quanto mais longo o prefixo compartilhado de dois GeoDNAs, mais perto estão as respectivas coordenadas no mapa (infelizmente, GeoDNAs com prefixos totalmente distintos também podem estar fisicamente próximos; localidades próximas ao meridiano de Greenwich são um exemplo clássico). Portanto, ordenando os registros por GeoDNA, as localidades próximas tenderão a ficar em linhas adjascentes. Para a integração, usaremos o recurso Add column by fetching URLs, clickando em header de qualquer coluna (a coluna não importa pois, de qualquer maneira, usaremos dados de duas):
No diálogo que aparecer, colamos o seguinte código (atenção para o nome das colunas com as respectivas coordenadas):
'http://127.0.0.1:3000/?lat='+ row.cells['latitude'].value +'&lon='+ row.cells['longitude'].value
Não precisamos do throttle delay, pois o nosso webservice é local. O diálogo deve ficar assim (nada de clickar em OK, ainda):
Agora, certifique-se de que você tem os módulos Mojolicious e Geo::DNA instalados, e rode no terminal:
perl -MGeo::DNA -Mojo -E 'a("/"=>sub{my$s=shift;$s->render(json=>{geocode=>Geo::DNA::encode_geo_dna($s->param("lat"),$s->param("lon"))})})->start' daemon
Se preferir, aqui está a versão "por extenso":
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #!/usr/bin/env perl use Geo::DNA qw(encode_geo_dna); use Mojolicious::Lite; any '/' => sub { my $self = shift; $self->render(json => { geocode => encode_geo_dna( $self->param('lat'), $self->param('lon'), ), }); }; app->start; |
Uma vez iniciado o webservice, clique em OK lá no diálogo do Google Refine e aguarde. Mesmo sem o delay, é relativamente lento; porém o custo/benefício dessa gambiarrinha é, evidentemente, favorável
DBIx::Class HATE

Exercendo a minha liberdade de expressão, vou tentar explicar por que raios, afinal, eu detesto tanto o DBIx::Class. Ninguém é obrigado a concordar comigo, mas espero que os que leiam, reflitam.
Sem dúvida, é um treco robusto e consistente, mas já dizia o Oscar Wilde: "Consistency is the last refuge of the unimaginative." Agora eu, felizmente (ou não), tenho imaginação de sobra. Sou conhecido por minhas gambiarras atrozes. Sou um péssimo programador, a ponto de ter largado a faculdade de computação no quarto ano e ter me formado em desenho industrial
O que gosto no Perl é a sua capacidade de ser um master-glue, amarrando pedaços desconexos numa "coisa" que, magicamente, atende às necessidades.
Como bem ilustra o fabuloso poema "Black Perl", o Perl é livre de preconceitos e purismos. Posso "furar" a privacidade dos atributos do Moose, subverter o MVC com o Mojolicious, além de fazer todo tipo de monkeypatching bizarro e ad-hoc.
Aí entra o DBIx::Class. Seu glorioso dbicdump já ofende os meus olhos:
# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-24 13:26:20 # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:qqyhu9B0Vr7l1KsQ6CG7Jw
Desgraçado, como assim?! Em primeiro lugar, a caralhada de classes (uma por tabela?!), para mim, por si só, é uma coisa javesca. E, em segundo lugar, o mentecapto é prolixo e nem posso ousar de intervir com o Perl::Tidy para deixar o código legível do jeito que gosto. Sem falar que o schema é "lossy" e perde várias informações.
Mas isso é o de menos. Não consigo imaginar um workflow que me agrade. Eu gosto do pgAdmin e do HeidiSQL e costumo elaborar as minhas tabelas e queries nessas GUIs. Isso me dá um retrabalho na hora de "portar" para Perl (diferentemente do DBI, aonde é só copiar e colar, ráááá).
E olha só, CRUD não é o foco do meu trabalho. Às vezes, preciso dar uma cutucada no banco de dados, ou então um grande processamento em lote. Mas variedade de queries? Não me pertence.
Aliás, um ponto importante: em geral, sou "equipe de um homem só". E Perl dá um grande poder nas mãos das equipes de um homem só. Não preciso de um código de fácil manutenção. Eu assumo a penalidade e o risco de me perder nas próprias queries quilométricas. Me divirto muito com isso.
Nesse ponto, DBIx::Class me parece coisa de tiozão. Me lembra aqueles emails corporativos aonde a assinatura é 100x maior do que a mensagem, sendo o schema a assinatura e o uso da mesma, a mensagem
Perl e módulos na $HOME
Mas, por quê?!
Qual o sentido de alguém instalar os módulos dentro do seu $HOME? Normalmente, uma das duas:
- Você não tem permissão suficiente para instalar no próprio sistema (você está mexendo num servidor ou coisa do tipo);
- Você está desenvolvendo a bagaça, e quer manter a versão bleeding edge só para você.
Bom, se for o 2-o caso, existe o tal do perlbrew.
Mas eu gosto das instalações locais e as uso desde '97. E o local::lib foi uma mão na roda e tanto! Então, vamos lá:
Passo-a-passo:
Download do local::lib:
curl -LO http://search.cpan.org/CPAN/authors/id/A/AP/APEIRON/local-lib-1.008004.tar.gz tar xzf local-lib-1.008004.tar.gz cd local-lib-1.008004/
Instalação do local::lib:
perl Makefile.PL --bootstrap=$HOME/perl5 make make test make install
Ativando local::lib na inicialização da shell (Bash):
cat << PROFILE >> $HOME/.profile export PERL_MM_USE_DEFAULT=1 eval \$(perl -I\$HOME/perl5/lib/perl5/ -Mlocal::lib) PROFILE
Agora, é hora de logoff/logon para quem está acostumado com o Windows, ou então:
. $HOME/.profile
Prosseguindo com a configuração/teste do CPAN (essa configuração faz as dependências serem instaladas automaticamente):
cpan cpan[1]> o conf prerequisites_policy follow cpan[2]> o conf commit cpan[3]> install App::cpanminus cpan[4]> quit
Enumerando os módulos instalados:
perl -MExtUtils::Installed -E 'chdir $ENV{PERL_LOCAL_LIB_ROOT}; my $i = new ExtUtils::Installed; say for $i->modules'
cpanminus permite atualizar todos de uma vez, de uma maneira eficiente:
perl -MExtUtils::Installed -E 'chdir $ENV{PERL_LOCAL_LIB_ROOT}; my $i = new ExtUtils::Installed; say for $i->modules' | cpanm



