Posts de ‘Tiago Motta’

[Tiago Motta] Alias e functions para seu dia-a-dia com git

Thursday, March 12th, 2009

Embora o Git seja uma poderosa ferramenta de controle de versão, a quantidade de passos que é necessário para executar alguns procedimentos básicos pode dificultar um pouco o dia-a-dia do desenvolvedor. Principalmente se o projeto em questão não requer todas as funcionalidades que o git oferece para compartilhar código entre grandes equipes espalhadas.

Felizmente é possível criar alguns alias e functions para facilitar o seu uso. Dessa forma podemos utilizar o git de maneira mais simples, e quando houver a necessidade de usar um dos seus recursos avançados, basta não usar esses atalhos. Segue abaixo os que tenho utilizado:

gitshazam

Adiciona todos os arquivos criados, modificados, deletados e depois comita para o repositório local com a mensagem informada.

alias gitshazam='git add -u && git add . && git commit -m'

Exemplo de uso:

gitshazam 'Minha mensagem de commit'git push

gitcoleradodragao

Sincroniza com o repositório remoto as informações dos branchs existentes e os exibe, mostrando também em que branch você está desenvolvendo atualmente.

alias gitcoleradodragao='git fetch && git branch -a -v'

gitpodediamante

Cria um branch local com o nome informado e o associa ao branch de mesmo nome no repositório remoto.

function gitpodediamante() { git checkout --track -b $1 origin/$1; }

Exemplo de uso:

gitpodediamante 'historia-22'

Agradecimentos especiais à galera da minha equipe que tem me ajudado bastante com o git e aguentado minhas reclamações.

[Tiago Motta] Duck typing e os testes de aceitação automáticos

Thursday, February 26th, 2009

Uma discussão frequente em minha equipe é sobre maneiras de garantir que nossos sistemas estejam funcionando adequadamente por meio de testes automáticos. Em nossos ultimos projetos temos conseguido alcançar 100% de cobertura de testes unitários, e nos sistemas legados temos aumentado a cobertura aos poucos.

No entanto, estamos cientes de que nem 100% de cobertura de testes unitários garantem a ausência de bugs. Isso nos remete aos testes de aceitação automáticos, não só por causa do funcionamento das telas e fluxos de navegação, mas também dos possíveis problemas que um refactoring com um pouco menos de atenção pode causar em uma linguagem baseada em duck typing.

Darei um exemplo em ruby para ilustrar um bug que pode ser introduzido sem que seus testes unitários percebam. Digamos que você tenha as seguintes classes:

class Filme < ActiveRecord::Base do  def alugar    self.estoque.remover self  endendclass Estoque < ActiveRecord::Base do  def remover(filme)    # ...  endend

No seu spec que verifica o método alugar da classe Filme você colocaria um mock mais ou menos como mostrado abaixo:

@estoque = mock_model(Estoque)@estoque.should_receive(:remover).with(@filme).once

Um dia você percebe que o nome do método remover não está explicando muito bem o significado, e resolve fazer um refactoring renomeando-o para remover_fita_do_filme.

Você altera os testes da classe Estoque, renomeia o método e logo depois recebe uma ligação de uma empresa de telefonia te oferecendo serviços excepcionais que você nunca precisou. Puto da vida você desliga o telefone, roda os testes e todos passam! Serviço feito!

Perceberam o problema? Sem um teste de aceitação automático ou no mínimo um teste manual, você não perceberia que a classe Filme continua referenciando o método antigo, remover, da classe Estoque. Esse tipo de problema não passaria em uma linguagem que não segue duck typing, pois a etapa de verificação de código da compilação serviria como uma espécie de teste unitário de tipos e nomes.

Um problema mais evidente seria um método que recebe um objeto que precisa ter determinado método, ou seja implementar determinada interface. Veja o exemplo abaixo, que mostra que ao alugar um item, no caso um Filme, adiciona-se todas as tags dele às tags preferenciais do cliente:

class Cliente < ActiveRecord::Base do  def alugar(item)    #...    adiciona_tags_preferenciais item.tags  endendclass Filme < ActiveRecord::Base do  def tags    # ...  endend

Se mudarmos o nome do método tags em Filme e também corrigirmos o uso de dele no método alugar da classe Cliente tudo funcionará bem. Mas, como usamos duck typing não temos como garantir que outros objetos, digamos alugáveis, tenham sido alterados. Se por exemplo esta locadora também alugas livros, teríamos um erro evidente:

class Cliente < ActiveRecord::Base do  def alugar(item)    #...    adiciona_tags_preferenciais item.tags_principais  endendclass Filme < ActiveRecord::Base do  def tags_principais    # ...  endendclass Livro < ActiveRecord::Base do  def tags    # ...  endend

Esse tipo de erro só seria pego num teste manual se lembrássemos de testar o aluguel de livros também. Por isso é tão importante o teste de aceitação automático em uma linguagem com duck typing. Em Java por exemplo, que a interface precisa ser explícita, o erro seria pego no que poderíamos considerar o teste unitário que a compilação executa. As classes Filme e Livro implementariam a interface Alugavel por exemplo:

public interface Alugavel {  List tagsPrincipais();}

Mas é claro que o uso de uma liguagem menos dinâmica não remove a necessidade de testes de aceitação automáticos. O valor desse tipo de testes é independente da linguagem, e sua implementação é importantíssima para garantir fluxos de navegação e integração entre os diversos componentes do sistema.

[Tiago Motta] Plugin do rails para copiar erros de um model para outro

Thursday, February 19th, 2009

Em um projeto pessoal precisei desenvolver uma maneira de copiar os erros de um model para outro. Como é uma funcionalidade que outrora já havia precisado, aproveitei para criar um plugin e disponibilizá-lo para quem mais tiver esse mesmo problema.

O plugin está disponível no GitHub pelo endereço http://github.com/timotta/copy_errors_from/tree/master. Para instalar no seu projeto basta rodar a seguinte linha:

script/plugin install git://github.com/timotta/copy_errors_from.git

Após instalar todos os seus models terão o método copy_errors_from, que pode ser utilizado como mostrado abaixo:

> filme = Filme.new :titulo => 'Corra que a polícia vem aí'> ator = Ator.new> filme.atores.push ator> filme.save #return false> filme.errors.entries #return []> ator.errors.entries #return [['nome','Não pode ser vazio']]> filme.copy_errors_from ator> filme.errors.entries #return [['ator_nome','Não pode ser vazio']]> filme.errors.on(:ator_nome) #return 'Não pode ser vazia'

[Tiago Motta] Cuidado ao cachear named_scope no Rails

Friday, January 23rd, 2009

Deve-se tomar cuidado ao cachear o acesso a um named_scope pois ele tem um carregamento de forma preguiçosa, o famoso lazy load. Assim, o código abaixo gravaria no cache não os atores femininos, mas sim todos os atores.

class Filme    #...  def atores_femininos_cacheado    Rails.cache.fetch("#{self.id}:atores-principais") { self.atores.femininos }  end

class Ator  #...  named_scope :femininos, :conditions => { :sexo => 'F' }

Se você observar o log, verá que o rails executará a query sem as condições definidas na classe Ator para o named_scope :femininos. E que após ser cacheado, será essa a query que deixará de ser executada.

A query para buscar atores femininos do named_scope só será realmente executada quando algum método do objeto retornado pelo método .atores.femininos for chamado. Veja no exemplo abaixo:

@filme = Filme.find_by_id 10filme.atores_femininos_cacheado.last

Ou seja, o cacheamento não está servindo ao propósito definido. Uma solução para isso é cachear diretamente o resultado de um find, como pode ser visto abaixo:

class Filme    #...  def atores_femininos_cacheado    Rails.cache.fetch("#{self.id}:atores-principais") do      Ator.find_all_by_filme_id self, :conditions => { :sexo => 'F' }    end  end

[Tiago Motta] PGError com Rails no Heroku

Wednesday, January 21st, 2009

Essa dica é para quem utiliza o Heroku Garden para desenvolver suas aplicações rails. Ao rodar os specs diretamente da interface web do Heroku pode ser que um dia você se depare com o seguinte erro:

PGError: ERROR: relation "filmes" does not exist: SELECT a.attname, format_type(a.atttypid, a.atttypmod),d.adsrc, a.attnotnullFROM pg_attribute a LEFT JOIN pg_attrdef dON a.attrelid = d.adrelid AND a.attnum = d.adnumWHERE a.attrelid = 'filmes'::regclassAND a.attnum > 0 AND NOT a.attisdroppedORDER BY a.attnum

O erro acontece porque o link para rodar as migrations, que aparece na interface do heroku, não as executa no ambiente de testes. Portanto, é preciso abrir a tela do rake e executar as seguintes linhas antes de rodar seus specs.

db:migratedb:test:prepare

UPDATE

Descobri outro problema do Heroku. O schema.rb é criado e alterado no diretório tmp. Dessa forma para que o PGError não dê novamente em outra atualização do banco, você precisa copiá-lo para o diretório db e então no console do rake rodar:

db:test:clone

[Tiago Motta] Imagem de fundo no captcha gerado pelo simple_captcha

Thursday, October 9th, 2008

O simple_captcha, plugin de geração de captcha para rails, não possui opção para colocar um imagem de fundo na imagem gerada. Mas fazer isso não é nenhum bicho de sete cabeças, basta alterar algumas partes chaves do plugin utilizando um pouco de rmagick. Vou mostrar aqui mais ou menos como fiz isso em um recente projeto.

No arquivo simple_captcha_image.rb basta alterar o método generate_simple_captcha_image para que na criação da imagem seja colocado o fundo desejado. Veja o exemplo de código abaixo:

def generate_simple_captcha_image(options={})  #:nodoc

  fundo = Magick::Image.read(url_da_imagem_de_fundo).first  preenchimento = Magick::TextureFill.new(fundo)

  @image = Magick::Image.new(197, 45, preenchimento) do     self.format = 'JPG'  end

  # ...

Repare que existe ali uma chamada ao método url_da_imagem_de_fundo que no caso é um método que pega o path de uma imagem aleatória. Dessa forma o captcha poderá ter vários fundos diferentes. Veja um exemplo de como pode ser a implementação desse método:

  def url_da_imagem_de_fundo    "#{RAILS_ROOT}/vendor/plugins/simple_captcha/assets/imgs/picture_" + (rand(9)+1).to_s + ".jpg"  end

Foi preciso também comentar a chamada ao método que aplica estilo e distorção à imagem, mas não sei bem se isso é necessário. Como fiz isso, precisei alterar o método append_simple_captcha_code que escreve o texto na imagem, de forma a utilizar diversos tamanhos, posições e fontes diferentes.

[Tiago Motta] Validação no lado cliente com form_remote_tag

Wednesday, October 8th, 2008

Para fazer validações de formulário no lado cliente basta informar ao método form_tag ou ao form_for, utilizando o parâmetro :onsubmit, o código javascript que se deseja executar para validá-lo. Veja no exemplo:

  <% form_tag "/entrar", :onsubmit => 'return valida(this)' do %>      <h1>ok</h1>  <% end %>

Contudo para a tag form_remote_tag e form_remote_for não há a possibilidade de utilizar o parâmetro :onsubmit. Isso porque o formulário gerado pelo rails já possui a ação onsubmit definida com o código que fará o acesso remoto. Para colocar então sua validação antes deste código deve-se usar o parâmetro :before como é mostrado no exemplo:

  <% form_remote_tag :url => "/entrar",         :update => 'meu_div',         :before => 'if( !valida(this) ) return false' do %>      <h1>ok</h1>  <% end %>

Repare que é importante que haja o “return false” no caso de não ter sido validado, caso contrário ele executará o restante do código colocado pelo rails no onsubmit.

[Tiago Motta] Impedindo redeclaração de objetos javascript

Thursday, October 2nd, 2008

Ao importar duas vezes um mesmo arquivo .js em uma página, as funções, protótipos e objetos são redeclarados. Se for necessário que um determinado objeto mantenha seu estado durante as iterações assíncronas dessa página, perde-se este estado na segunta importação do arquivo .js.

Veja por exemplo o objeto abaixo, que nada mais é que um repositório de outros objetos. Tá ta ta, não é um exemplo que tenha tanta utilidade, mas vá lá, é só pra explicar o mecanismo.

var Repositorio = {    sequencia:0,    lista:[],    registrar:function(obj) {        var id = ++this.sequencia;        this.lista[ id ] = obj;        return id;    },       recuperar:function(id) {        return this.lista[ id ];    }}

Agora estando esse código num arquivo por exemplo repositorio.js teríamos o problema relatado com o código a seguir:

<script src="repositorio.js"></script><script>    Repositorio.registrar( new Object() );    Repositorio.registrar( new Object() );</script><script src="repositorio.js"></script><script>    alert( Repositorio.recuperar( 1 ) );</script>

Para impedir essa redeclaração do objeto, e os possíveis erros que ela pode causar, basta tentar utilizá-lo dentro de um bloco try e fazer sua declaração dentro do bloco catch. Ou seja, ele só será declarado caso haja um erro na tentativa de usá-lo. Veja:

try{ Repositorio.existo(); }catch(e) {    Repositorio = {        sequencia:0,        lista:[],        existo:function(){},        registrar:function(obj) {            var id = ++this.sequencia;            this.lista[ id ] = obj;            return id;        },           recuperar:function(id) {            return this.lista[ id ];        }    }}

Mas aí você poderia me dizer: Pô pra que tudo isso, basta não importar duas vezes o javascript. Acontece que se você está criando uma biblioteca para ser distribuída para diversos sites e blogs, você não deve subestimar a “criatividade” dos utilizadores dela. Então, o melhor é se precaver.

[Tiago Motta] Cópia exclusiva de coleção

Thursday, September 18th, 2008

Por serem de díficil identificação, problemas de concorrência acabam indo pra produção sem sequer serem notado em ambiente de testes e desenvolvimento. Erros acabam ocorrendo de maneira intermitente, causando dores de cabeça e acessos de insanidade nos desenvolvedores. Martin Fowler em seu livro Patterns of Enterprise Application Architecture identifica algumas soluções para evitar problemas de concorrência relativos a integridade de dados. Contudo há um outro problema que não está catalogado neste livro: A concorrência de informações na memória.

Ao guardar objetos em uma coleção que é compartilhada por outras threads, é necessário tomar providências para que não sejam lançadas exceções inesperadas. A medida básica é garantir que os pontos de acesso às coleções compartilhadas devem obter exclusividade sobre seu uso com a diretiva synchronized. Veja abaixo um exemplo de classe que torna o uso de uma lista à prova de erros de concorrência:

public class ListaSegura {    private List lista = new ArrayList();    public void adiciona(Object objeto) {        synchronized (lista) {            lista.add(objeto);        }    }    public void remove(Object objeto) {        synchronized (lista) {            lista.remove(objeto);        }    }    public void listar() {        synchronized (lista) {            for (Object o : lista) {                System.out.println(o);            }        }    }}

É uma solução simples e que resolve em parte o problema. Contudo na maioria das vezes não podemos obter a exclusividade para iterar sobre uma coleção. No caso mostrado acima isso é possível pois estamos apenas imprimindo o objeto. Mas existem alguns casos em que obter essa exclusividade para a iteração nos traria alguns problemas. Esses casos estão descritos abaixo:

1- Alteração da coleção: No caso de você iterar pela coleção para remover ou adicionar algum objeto a ela. A exclusão ou adição ocorreria no meio da iteração e com isso seria lançada uma exceção de concorrência.Um exemplo simples disso são classes que gerenciam cache e precisam periodicamente remover objetos expirados.

2- Iteração prolongada: Quando a coleção possui objetos demais ou o processo executado durante a iteração é lento, tornando o tempo de exclusividade total muito longo. Isso faria com que o restante do sistema que precisasse utilizar essa coleção ficasse muito tempo aguardando pela exclusividade terminar. Um exemplo comum é a execução de métodos que acessem banco de dados dentro da iteração de um objeto exclusivo.

Para resolver esses dois casos identifiquei o padrão de desenvolvimento Cópia Exclusiva de Coleção. A idéia é obter a exclusividade da lista somente para fazer uma cópia dos itens em outra coleção e então poder iterar sobre esta cópia sem muitas preocupações. Abaixo mostro um exemplo de uma classe Armario que possui muitas Coisas. Se alguma coisa for lixo, ela deve ser removida na execução do método removeLixo.

public class Armario {    private List coisas = new LinkedList();    public void removeLixo() {        List copia = lista();        for (Coisa coisa : copia) {            if (coisa.isLixo())                remove(coisa);        }    }        public List lista() {        List copia = null;        synchronized (coisas) {            copia = new ArrayList(coisas.size());            copia.addAll(coisas);        }        return copia;    }    public void adiciona(Coisa coisa) {        synchronized (coisas) {            coisas.add(coisa);        }    }    public void remove(Coisa coisa) {        synchronized (coisas) {            coisas.remove(coisa);        }    }}

Com isso o tempo de exclusividade fica restrito ao tempo da cópia para a outra coleção, que ocorre no método lista. Dessa forma temos uma folga no tempo de iteração total e a possibilidade de rearranjar a coleção, adicionando ou removendo itens a ela. É importante ressaltar que o melhor jeito de evitar o calafrio de receber um java.util.ConcurrentModificationException é evitar a utilização de objetos compartilhados entre threads. Quando isso não é possível, o jeito é utilizar um padrão como Cópia Exclusiva de Coleção.