Mostrando Artigos com 1 tag: Dicas

encontrando uma sarna para se coçar

Posted by joaolins, Tue Dec 18 19:36:00 UTC 2007

Muita gente se beneficia de software open source e ferramentas livres. Quando isso acaba se tornando constante pode acabar batendo aquele sentimento de que é necessário dar alguma coisa em troca, contribuir. É ruim pensar que existem pessoas trabalhando por trás daquilo e você está simplesmente mamando. Isso certamente não passa pela cabeça de todo mundo, mas provavelmente da maioria dos mineradores.

Bom, eu vou contar a história que aconteceu comigo.

Tudo começou quando tive um pequeno problema com um sistema que estou fazendo com Rails. Lá é preciso fazer upload de um arquivo XML em um dado momento. Até ai tudo bem:

result = xml_parser.parser(params[:file_upload])

Isso funcionou bem. Todos os testes passaram e eu nem me preocupava com o tipo de objeto que vinha dentro do params[:file_upload], tinha em mente que sempre seria um TempFile.

Qual não foi minha surpresa quanto foi feito o deploy em produção: BOMBA! Acabei descobrindo que, nas versões de Ruby anteriores à 1.8.6, somos obrigados a chamar open no TempFile.

result = xml_parser.parser(params[:file_upload].open)

Aparentemente é uma modificação bem pequena. Desta vez a surpresa foi um pouco menor, já que aconteceu em ambiente de testes. Mas não pude deixar de me assustar quando um erro saltou na minha cara:

NoMethodError: private method `open' called for #<StringIO:0xb7c35020>

Depois da mão na cabeça, veio naturalmente um pouco de depuração e inspeção. Notei que o params[:file_upload] estava retornando um StringIO e que a chamada ao método open, que na documentação do ruby core aparece como publico e na verdade é privado, dava o erro acima. Resolvi tirar a história a limpo. Dei uma lida no código do Rails e acabei descobrindo que se o upload for de arquivos menores do que 10KB um StringIO é retornado e não um TempFile como eu esperava.

Neste ponto eu já sabia a razão de todos os meus problemas e só faltava resolver, o que fiz foi o seguinte:

if params[:upload_file].instance_of?(StringIO)
    result = xml_parser.parser(params[:upload_file])
else
    result = xml_parser.parser(params[:upload_file].open)
end

Isso funciona para todas as versões de Ruby e tamanhos de arquivo, mas eu não fiquei satisfeito com a solução porque não a achava limpa o bastante e porque esse comportamento do Rails não era documentado.

Meu código estava funcionando, mas eu não estava satisfeito. Lembra da história de contribuir e parar de mamar? Pensei: “é hora de largar a teta!” Eu estava disposto e há algum tempo já vinha procurando sarna para me coçar, foi só procurar um pouco na internet para saber o que eu precisava fazer para contribuir com o Rails. Encontrei os slides encorajadores de Josh Susser e arregacei as mangas para por as mãos à obra.

Depois de ter lido o código, eu sabia exatamente como resolver o problema. Escrevi um email para a rails-core falando do problema e fazendo uma proposta para que o Rails retornasse apenas TempFile, ao invés de tentar fazer uma otimização que pode fazer muita gente ter que coçar a cabeça para resolver . Alguns emails rolaram na lista e o primeiro a me responder foi o Michael Koziarski que escreve para o The Rails Way.

Koz achou a minha proposta interessante, mas ele não conseguiu entender que tipo de bug aquela otimização poderia resolver. Enviei uma resposta e tentei explicar tudo de forma mais detalhada em outro email para a lista. Foi quando Jonathan Yurek me respondeu que todos os meus problemas poderiam ser solucionados de uma forma bem simples usando o poder libertador do duck typing:

result = xml_parser.parser(params[:upload_file].read)

A solução proposta pelo Yurek funciona perfeitamente. Eu não tinha prestado atenção, mas o método read que funciona no StringIO, também pode ser usado para o TempFile, pois ele é um Delegate de File que herda de IO e possui o método read.

Depois disso Koz respondeu que ainda acharia interessante que isso fosse documentado em algum lugar da API do Rails e que ele poderia aplicar a modificação caso alguém fizesse o patch. Hongli Lai também escreveu para a lista informando sobre um post que fez em seu blog ao ter um problema bem parecido com o que eu tive e pareceu solidário a minha proposta.

Ainda não mandei o patch para deixar claro o comportamento do Rails ao fazer uploads. Explicar esse comportamento não é difícil. Na verdade o maior problema é saber exatamente o melhor local para colocar a informação. Alguma sugestão?

1 comment | Filed Under: | Tags: Dicasdicas