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.


remove trailing whitespace in vim

Here's a quick tip to highlight trailing whitespace - put this on your .vimrc:

match Todo /\s\+$/

This will flag them the same way as TODO or FIXME, with a ugly yellowish mark:

preview

To remove them, run this on normal mode:

:%s/\s\+$//e

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.


new layout new life

Finally I got rid of enki's default theme!

What do you think? All critiques/comments will be more than welcome!!!


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.


Pareando Remotamente

Não preciso tecer comentários sobre as vantagens da programação em par. Se você ainda tem dúvidas, leia isto

pair - taken from http://www.flickr.com/photos/cobalt/677911778/sizes/m/

Às vezes não é possível a presença física de um dos pares, por diversos motivos. Para esse tipo de situação, screen! (contando que você consiga usar editor modo texto)

Não vou me aprofundar no screen em si, deixando uma receita de bolo para os impacientes:

Você vai precisar de uma máquina acessível por ssh, com o screen instalado - praticamente todas as distribuições de linux possuem um pacote pro screen.

Receita de bolo

  • acesse a máquina remota
  • execute screen
  • ctrl + a :multiuser on (sim, este comando é bem chato para quem não está acostumado, isso vai colocar o screen no modo de compartilhamento de tela
  • peça ao outro desenvolvedor para acessar a mesma máquina (com o mesmo usuário, sei que funciona com usuários diferentes, mas ainda não testei)
  • o outro desenvolvedor executa screen -rx e voilá, já temos uma tela compartilhada entre os pares.

Algumas dicas do screen

  • ctrl + a d => desconecta o terminal, e deixa ele rodando no servidor. Isso é extremamente útil em conexões que ficam caindo o tempo todo...
  • ctrl + a c => cria um novo terminal virtual, que pode ser acessado usando.....
  • ctrl + a 0 => acessa o terminal virtual #0
  • ctrl + a 1 => acessa o terminal virtual #1 e assim respectivamente...
  • ctlr + a ? => acessa a ajuda do screen

Viu como é fácil?

Agradeço ao Jeff Schoolcraft pela dica, me ajudou muito a começar a brincar com cucumber / rspec.


food for thought

How can we ignite somewhat sleepy/lazy brains?

I just had an insane idea - no guarantees - you'll need:

  • a small dev team
  • a whiteboard
  • a sudoku puzzle generator/magazine/whatever

Instructions:

  1. Write down the sudoku puzzle on the whiteboard and cover it with something.
  2. Mark the time
  3. Let the team solve it
  4. Mark the time elapsed
  5. Profit

Would it work? Any neat ideas to share?

Inspiration shamelessly taken from softwarebyrob.com


on the cloud

Recently I moved to the cloud!

The experience is amazing - Rackspace has really beefed up their servers, all my services feel snappier. No turning back.


new blog engine

Yeah, finally I've moved from jekyll - jekyll is awesome, but there are times that you don't have ssh access, and want to blog. This is my main motivation to go to enki, a very smart blog engine written in ruby/rails. Stay tuned!

I know that the visual is horrible, I will fix it this ASAP


gitosis setup step-by-step

GIT This is the first post of the git series - small useful tips for the everyday git.

This tutorial is about [Gitosis] setup. From the project's readme:

Gitosis aims to make hosting git repos easier and safer. It manages multiple repositories under one user account, using SSH keys to identify users. End users do not need shell accounts on the server, they will talk to one shared account that will not let them run arbitrary commands.

For this recipe, we will use some conventions:

  • user@local$ for commands that should be run at your local machine.
  • user@srv$ for commands that should be run at your server.
  • git@srv$ for commands that should be run with the git user (we'll create this later, delete this user or use another name if you already has it on your server).
  • all root access will be done through sudo.
  • lines ending with \ must be typed in one line

We will need a linux server; my setup will be based on a ubuntu system, but you can use almost any *nix flavor.

You will need to have an ssh server installed as pre-requisite - run sudo apt-get install ssh to install it. (thanks DJC)

  1. Install Compilation Tools & required libs for git

    user@srv:~$ sudo apt-get install build-essential libssl-dev \
    zlib1g-dev libcurl4-openssl-dev libexpat-dev
    
  2. Download & uncompress Git sources

    user@srv:~$ wget http://kernel.org/pub/software/scm/git/git-1.6.4.4.tar.bz2
    user@srv:~$ tar -jxvf git-1.6.4.4.tar.bz2
    
  3. Build & install git

    user@srv:~$ cd git-1.6.4.4
    user@srv:~/git-1.6.4.4$ make prefix=/usr/local NO_TCLTK=1 all
    user@srv:~/git-1.6.4.4$ sudo make prefix=/usr/local NO_TCLTK=1 install
    user@srv:~/git-1.6.4.4$ cd 
    
  4. Install required python libs

    user@srv:~$ sudo apt-get install python-setuptools
    
  5. Download & install gitosis

    user@srv:~$ git clone git://eagain.net/gitosis.git
    user@srv:~$ cd gitosis
    user@srv:~/gitosis$ sudo python setup.py install
    
  6. Create our git user

    user@srv:~$ sudo useradd -s /bin/bash -U -d /var/lib/git -m -r git
    
  7. Generate the gitosis-admin ssh key (if you don't have one already)

    user@local:~$ ssh-keygen -t rsa -C user
    

    press enter for the default location, then provide it with a passphrase

    copy the generated public key to server

    user@local:~$ scp ~/.ssh/id_rsa.pub user@srv:/tmp/user.pub
    
  8. Log in as the newly created git user

    user@srv:~$ sudo su - git
    git@srv:~$ 
    
  9. Initialize gitosis

    git@srv:~$ gitosis-init < /tmp/user.pub
    Initialized empty Git repository in /var/lib/git/repositories/gitosis-admin.git/
    Reinitialized existing Git repository in /var/lib/git/repositories/gitosis-admin.git/
    
  10. Adjust some permissions on admin repository

    git@srv:~$ chmod +x ~/repositories/gitosis-admin.git/hooks/post-update
    
  11. Clone the gitosis-admin repository

    user@local:~$ git clone git@srv:gitosis-admin.git
    Initialized empty Git repository in /home/user/gitosis-admin/.git/
    remote: Counting objects: 5, done.
    remote: Compressing objects: 100% (4/4), done.
    remote: Total 5 (delta 0), reused 5 (delta 0)
    Receiving objects: 100% (5/5), done.
    
  12. Voilà! Your gitosis setup is working! Let's take a look at gitosis's structure:

    gitosis.conf
    keydir/
    keydir/user.pub
    
  13. To finish our recipe, lets add a new user & a new repository
    Let's call our new user john
    Get john's ssh public key and put it inside keydir/john.pub
    Add to gitosis.conf:

    [group new-repo]
    writable = new-repo
    members = user john
    
  14. As gitosis is managed by git itself, let's commit our changes:

    user@local:~/gitosis-admin$ git status
    # On branch master
    # Changed but not updated:
    #   (use "git add <file>..." to update what will be committed)
    #   (use "git checkout -- <file>..." to discard changes in working directory)
    #
    # modified:   gitosis.conf
    #
    # Untracked files:
    #   (use "git add <file>..." to include in what will be committed)
    #
    # keydir/john.pub
    no changes added to commit (use "git add" and/or "git commit -a")
    user@local:~/gitosis-admin$ git add keydir/john.pub gitosis.conf
    user@local:~/gitosis-admin$ git commit -m "added new-repo + john keys"
    [master ad62139] added new-repo + john keys
     2 files changed, 4 insertions(+), 0 deletions(-)
     create mode 100644 keydir/john.pub
    user@local:~/gitosis-admin$ git push origin master
    Counting objects: 8, done.
    Compressing objects: 100% (5/5), done.
    Writing objects: 100% (5/5), 1.02 KiB, done.
    Total 5 (delta 0), reused 0 (delta 0)
    To git@srv:gitosis-admin.git
       bdc4dbc..ad62139  master -> master
    
  15. Finally, let's john push his work to the shiny new repository:

    john@local2:~/new-repo$ git remote add origin git@srv:new-repo.git
    john@local2:~/new-repo$ git push origin master
    Counting objects: 3, done.
    Delta compression using up to 2 threads.
    Compressing objects: 100% (2/2), done.
    Writing objects: 100% (3/3), 241 bytes, done.
    Total 3 (delta 0), reused 0 (delta 0)
    To git@srv:new-repo.git
      * [new branch]      master -> master
    
  16. C'est fini! A complete setup running. Enjoy!
    If you have any doubts / corrections, please comment!