da teoria à prática - levenshtein

http://www.flickr.com/photos/crosswordman/3930231228/sizes/s/in/photostream/ Normalmente, nossa área possui duas vertentes totalmente polarizadas: os práticos e os acadêmicos.

Antes de mais nada, espera-se de um bom profissional um equilíbrio interessante entre conhecimento acadêmico (não necessáriamente adquirido na academia) e o conhecimento prático.

Ambas as coisas são boas, o problema é a falta de equilíbrio entre as duas!

Vez por outra, alguns problemas da vida real se mostram verdadeiros “cases” para a aplicação de coisas altamente teóricas da academia, e como não podia deixar de ser, fui vítima me deparei com uma dessas recentemente.

Como comparar um dado oficial com um dado digitado por uma pessoa? A resposta inicial seria bem simples, não?

nome1 = “Av. Morvan dias de Figueiredo, nº 3600”
nome2 = “Av. Morvan dias de Figueiredo, nº 3600”

nome1 == nome2
=> true

O fato é que as coisas não são tão simples assim na vida real:

nome1 = “Av. Morvan dias de Figueiredo, nº 3600”
nome2 = “Av. Morvan dias de figueiredo, nº 3600”
nome1 == nome2
=> false

Oops, mas não é o mesmo nome?

O fato é que não é tão trivial assim fazer comparações de strings; o caminho natural seria imediatamente colocar tudo em maiúsculas ou minúsculas e comparar; daí teriamos os acentos esquecidos; depois teríamos alguém mais criativo:

nome1 = “Av. Morvan dias de Figueiredo, 3600”
nome2 = “Av. Morvan dias de Figueiredo, N 3600”
nome1 == nome2
=> false

Ué... mas não é o mesmo nome, qual o problema de retornar false? Isso se torna um problema enorme, já que tecnicamente as duas strings representam sim a mesma coisa! Nesse caso seria muito mais interessante medir a similaridade entre essas strings. Como fazer isso?

Existe um algoritmo muito interessante que calcula a quantidade de alterações que eu tenho que fazer pra que um grupo de dados fique igual ao outro, mais conhecido como Distância Levenshtein. Neste caso, ao comparar as duas strings usando levenshtein, obteria o valor 2. (tenho que colocar a letra “N” e mais um espaço, para que o nome1 fique igual ao nome 2, logo duas alterações).

Para conseguir um índice de similaridade, nada melhor do que inventar uma escala e tomá-la como base: elegi minhas regras elementares e cheguei em uma escala em porcentagem, seguindo esta fórmula:

indice de similaridade = 1 - (distância / tamanho da primeira string)

Isso vai chegar a um número interessante, no nosso caso como a string possui 36 posições, vamos ter um valor de 0.9444444444, ou seja, quase 95% de semelhança!

Claro que a regra não é tão abrangente, mas para simples comparações já ajuda, e muito!

Como a maioria dos algoritmos, esse também já possuía implementação open-source. Existe uma gem de ruby chamada text que se encarrega de implementar coisas comuns a análise textual. Então, encapsulei o código de comparação da similaridade entre strings e coloquei na minha gem de utilidades a canivete.

Veja o resultado final:

require 'rubygems'
require 'canivete/similarity'

string1 = “Av. Morvan dias de Figueiredo,  3600”
string2 = “Av. Morvan dias de Figueiredo,  N 3600”
string1.similarity_rate(string2)
=> 0.944444444444

Claro que isso pode não servir pra diversos casos, mas é bom conhecer esses algoritmos!

Você conhece mais algum caso? Comente!


operações de carga com ruby

http://farm1.static.flickr.com/122/308587800_c8d0417f1e.jpg

Às vezes temos que fazer operações de carga / extração em bases de dados.

Como não podia deixar de ser, muitas vezes esse banco de dados é um banco totalmente legado, entupido de chaves compostas, e pra piorar a situação, concorrido entre diversas aplicações da empresa.

Como extrair dados sem impactar a performance do banco de origem?

Antes que você me fale que existem ferramentas de ETL, meu objetivo é mostrar alternativas, certo? Então, vamos lá.

Uma abordagem simples e interessante é uma implementação do modelo de produtor / consumidor, utilizando uma fila e duas threads:

require 'thread'
fila = Queue.new

TAMANHO_TOTAL = 100

produtor = Thread.new do
    # extrai os dados de algum lugar
    (1..TAMANHO_TOTAL).each do |i|
        # simulando trabalho
        sleep rand(1)
        fila << i
    end
end

consumidor = Thread.new do
    # array de trabalho
    buffer = []
    (1..TAMANHO_TOTAL).each do
        buffer << fila.pop
        # vamos processar de 4 em 4 items
        if buffer.size >= 4 or not produtor.alive?
            # faz alguma coisa com as tarefas
            puts buffer.inspect
            # limpa a fila
            buffer.clear
        end
    end
    # faz alguma coisa com as tarefas que sobraram
    puts buffer.inspect
    buffer.clear
end

consumidor.join 

Simples não? E você ainda ganha muito mais se executar esse mesmo código com jruby, graças ao suporte à threads da JVM.


DSLs externas

Um grande benefício trazido pelas idéias do Domain-Driven Design (DDD) é a diminuição da impedância na comunicação entre cliente e desenvolvedores, através de uma linguagem ubíqua. A ideia é que os termos do negócio sejam usados tanto por desenvolvedores, como por clientes.

Mas, que diferença isso faz afinal? Muita!

Os desenvolvedores, que por natureza tem enorme facilidade em adicionar complexidade, sofrem um efeito colateral muito interessante ao serem expostos à uma linguagem ubíqua: são obrigados a simplificar sua forma de pensar. Tarefas consideradas corriqueiras ganham mais atenção, ao passo que cenários onde a complexidade seria predominante, em um momento de inspiração, são reduzidas a pequenos incrementos.

Uma das formas de catalisar o entendimento do negócio do cliente é diminuir ainda mais o ruído durante o desenvolvimento, utilizando DSL’s.

DSL? De novo?

Assunto muito martelado, com aplicações bacanas tanto na teoria como na prática;

Apesar de possuirmos linguagens dinâmicas (ruby), flexíveis (lisp) e poderosas para criação de DSL’s (scala), às vezes se faz necessário criar linguagens totalmente fora do comum: mais declarativas e menos imperativas: as famosas DSL’s externas. Acredito que o exemplo mais popular de DSL externa seja a linguagem do cucumber, chamada gherkin:

Cenário: Adicionar um contato
    Dado que eu estou na página inicial
    E eu sou um usuário autenticado
    Então eu devo ver “adicionar novo usuário”

Muito legal de usar, mas como isso realmente funciona? Como fazer a minha própria DSL?

Esta madrugada, o @rodrigoy me mostrou o kanban_sketch, uma ferramenta interessante pra montar um painel de kanban a partir de uma DSL simples:

selected(A,B,R,T)
development:5
  [acceptance:1(C,Z)]
  [in progress:3(Q,Y,W)]
  [done!()]
deployment:3(E)
in production(F,K,L,M,N)  

isso deve produzir um resultado mais ou menos assim:

kanban_sketch

Interessante!

Por mais que nós desenvolvedores conseguíssemos entender bem um hash de ruby {:selected => [‘A’, ‘B’, ‘R’, ’T’] ......}, acredito que o formato anterior ainda traz maior legibilidade: muitas vezes vale o esforço!

E no quê consiste esse esforço?

Para conseguir transformar o texto solto em código, precisamos inicialmente fazer o “parse” deste texto - transformá-lo em uma estrutura normalizada, e já identificar possíveis erros de sintaxe.

Sintaxe?

Sim, por mais que muita gente já torça o nariz lembrando das aulas de português, o fato é que para que haja uma sintaxe, precisamos construir uma gramática, que diga quais serão as regras que nosso texto deve obedecer. A gramática do kanban_sketch está ficando assim:

grammar KanbanDSL

  rule stage
    (name (':' limit)? (cards / substages) ';')*
  end

  rule substages
    substage*
  end

  rule substage
    '[' name (':' limit)? cards ']'
  end

  rule limit
    [0-9]+ {
      def text_value
        text_value.to_i
          end
    }
  end

  rule cards
    '(' ((name [,]?)+)? ')'
  end

  rule name
    [a-zA-Z0-9\s]+
  end

end

Como você pode ver, uma gramática é realmente uma série de regras - literalmente :)

Em segundo lugar, após ter passado pelo parser, vamos obter uma AST, que não é nada além de uma árvore de sintaxe abstrata, que descreve os blocos tratados pelo parser com objetos, prontos para serem manipulados!

Acredito que a ferramenta mais famosa para isso seja o antlr - muito poderoso (podemos usar pra praticamente qualquer tipo de linguagem), e por consequência, extremamente complexo. Uma alternativa bem mais simples é o treetop, que utiliza uma abordagem diferente do antlr, preferindo clareza de código à performance. Esse tradeoff entre performance e complexidade, será abordado em um post futuro, se o tempo me permitir :)

Você encontrará mais detalhes sobre como usar o antlr aqui e o treetop aqui.

Uma vez que os desenvolvedores passam a dominar os termos do negócio, se tornam poderosos instrumentos de mudanças e melhorias, não só no software como também no negócio do cliente!

Agradeço ao @rodrigoy pela #provocaçãogratuita :D


pareando remotamente com tmux

Este artigo é uma continuação do pareando remotamente

No outro post, havíamos falado do screen, uma excelente ferramenta tanto para pareamento remoto, como para simplesmente garantir que os programas continuem rodando mesmo que a sua conexão ssh caia.

Porém, como nem tudo é perfeito, o screen tem suas limitações, e, no melhor estilo, surge na comunidade uma alternativa interessante: tmux

Se eu tinha falado de zilhões de atalhos para você conseguir fazer uma sessão de pair-programming remoto via terminal, sinto desapontá-lo, mas o tmux tornou tudo mais fácil:

Enquanto um usuário conecta no servidor e executa

tmux

O outro usuário conecta com o mesmo usuário e executa

tmux attach

O resultado é instantâneo, simples e eficiente. Tudo que você estava acostumado a usar no screen continua funcionando no tmux; a única diferença é que o atalho ctrl+a virou ctrl+b, por questões de interoperabilidade (o criador do tmux era usuário do screen e desenvolveu o tmux usando screen). Isso pode ser configurado, mas isso fica para outra oportunidade.

Acho que a principal coisa a destacar no tmux é o suporte a splits (uma beleza pra usuários de vim como eu), suporte a unicode e 256 cores; falando das partes internas, uma arquitetura 100% client/server, permitindo inclusive que as janelas possam ser movidas de uma sessão para outra, além de retirar um monte de funcionalidades obscuras presentes no screen, como suporte a terminais seriais e outras coisas exóticas. A descrição completa das diferenças você pode encontrar aqui

Já experimentou? Aposentei o screen por aqui...


batch - escalando um sistema sem fermento

No último Ruby + Rails no Mundo Real, tive a oportunidade de falar mais um pouco sobre processamento batch.

http://www.flickr.com/photos/scarlet_rose/354921254/sizes/s/

Por mais que nós desenvolvedores sempre busquemos maior performance em nossas aplicações web, muitas vezes, a melhor alternativa para obter melhores tempos de resposta acaba sendo diminuir o tamanho dessa resposta!

Parafraseando, se o relatório anual demora 12 minutos para ser processado, é provável que você consiga reduzir o tempo para apenas 1 minuto se você devolver um relatório mensal. Parece até imbecil, mas lidamos com tantos problemas absurdos no dia-a-dia que acabamos por diversas vezes nos esquecendo do óbvio. (é claro que estou ilustrando um cenário onde já fizemos melhorias, chegando a um ponto onde teoricamente não teríamos mais como melhorar o tal relatório).

Migrar um processo para tarefas em segundo plano permite que possamos devolver de imediato uma resposta ao nosso cliente web, e nos bastidores dividir uma tarefa grande em pedaços menores, que por sua vez podem ser divididos entre vários processos ou até máquinas. Dependendo do nosso nível de automação, podemos até disparar mais instâncias no amazon ec2, por exemplo, para ajudar no processamento, e desligá-las em seguida. Tarefas que demandariam um enorme investimento inicial se tornaram acessíveis a pequenos empreendedores, abrindo um leque de possibilidades quase infinito.

Para começar a usar processos em batch, você não precisa de ferramentas caras, ou de soluções de alta complexidade. Basta desacoplar serviços em pedaços do sistema que possam ser iniciados em uma outra linha de execução, quer seja usando threads, fibers, actors ou uma ferramenta de processamento batch como resque, ou em casos mais avançados, alguma implementação de MapReduce, como o hadoop.

E você, usa batch hoje em dia?

Veja os slides da palestra ou assista o vídeo da palestra.

A você que me aturouprestigiou no evento, muito obrigado!

Um agradecimento especial ao @agaelebe, que sempre tem feito as filmagens e nos ajudado a divulgar o guru-sp.


katch-up

I've just finished deploying of my new useless app - it's called katch-up.

The main idea came after a slow working month, endangered to be a short money month - I've caught myself several times recounting how many hours I still needed to work to fill my monthly contract, and believe me, this is not a good experience.

So, despite most of us (including me) are with lots of extra hours (all months), I will probably make something simple to improve the app experience, like give people ability to inform how many hours they have already worked - perhaps the app could warn when a predefined threshold is attained...

What do you think? Do you have any idea to share?

Thanks to @scalone for pairing with me at this app. We've done it in a kinda dojo form. Rewarding, as usual.


canivete released

Proudly, I give to thee canivete, the Ruby Ultimate Utils gem.

It doesn't ends with world's famine, but at least allows you to mark some methods of your ruby objects as deprecated and they will spit a warning every time you access them.

I think it has room for more - have you any idea to share?

install with gem install canivete or grab source code here

edit: I needed to change the name from ruutils as a gem with a very similar name exists (rutils) - thanks for pointing out jastix.