sexta-feira, 15 de janeiro de 2010

Diferenças entre ruby e python: Que perigo

Em ruby:

def a(z=[])
z.push 'a'
end
a # retorna ["a"]
a # retorna ["a"]
a # retorna ["a"]
a # retorna ["a"]

Em python:

def a(z=[]):
z.append('a')
return z
a() # retorna ['a']
a() # retorna ['a','a']
a() # retorna ['a','a','a']
a() # retorna ['a','a','a','a']

quarta-feira, 25 de novembro de 2009

Utilizando asserts para testar layouts

Um dos problemas encontrados em nossos testes de aceitação é a impossibilidade de validar a aparência exata do resultado final de uma determinada ação do usuário. Conseguimos validar com o watir se determinada div possui um texto, se determinado link está presente, mas nada impede que eles estejam escondidos, por trás de outro div, ou com letras da mesma cor do fundo. É sempre útil, mas não necessáriamente exato, sendo sempre um ponto de falha. Ainda mais em um sistema como o que estamos trabalhando em que o visual para o usuário tem uma grande importância.

Eu e o quixadá, mestre do javascript e meu par de hoje, trabalhamos em uma correção de bug visual, e como costumamos trabalhar com desenvolvimento outside-in, chegamos ao dilema de como criar um teste para garantir que a falha existia. A solução encontrada foi inserir asserts dentro do código javascript, tal qual a funcionalidade de asserts do java. O código da função assert ficou parecido com o mostrado abaixo:

function assert(mensagem,valorDesejado,valorRecebido) {
if( valorDesejado != valorRecebido ) {
var html = '<div class="warning">' + mensagem + '</div>';
$('body').append(html);
throw mensagem;
}
}

Em nosso caso, tinhamos que ter a certeza de que após um clique do usuário a barra de rolagem do elemento mantinha-se da mesma forma que anteriormente. Então o código ficou parecido com o mostrado abaixo:

$.get( url, function ( responseHtml ) {
var scrollAnterior = $('#opcoes').scrollTop();
substituiOpcoes( responseHtml );
assert( 'Deveria manter scroll igual', scrollAnterior, $('#opcoes').scrollTop() );
});

No teste de aceitação verificamos então que o texto 'Deveria manter scroll igual' não deveria aparecer. O código do step do cucumber utilizando o watir pode ser visto abaixo:

Then /^a barra de rolagem deveria permanecer na mesma posição$/ do
@browser.text.should_not include('Deveria manter scroll igual')
end

Com o teste pronto e falhando, aí sim corrigimos a função javascript substituiOpcoes(html) de forma a manter o scroll anterior.

Não é uma solução perfeita. Estamos pesquisando uma melhor, como pode ser vista no post Testes de aceitação automáticos para Flash com T-Plan Robot do Anselmo. Mas enquanto isso podemos evitar alguns pontos de falha visuais que costumávamos ter que testar manualmente. Dado que nosso sistema possui 100% de cobertura de testes unitários, somados a 152 cenários de teste de aceitação que abragem 1568 passos, acho que estamos indo por um bom caminho.

segunda-feira, 9 de novembro de 2009

sexta-feira, 30 de outubro de 2009

API rest para OpenSocial do Orkut com ruby

A documentação da API rest do OpenSocial do Orkut detalha muito bem as opções e formatos de retorno disponíveis porém é um tanto vaga sobre como fazer a autenticação necessária para usá-la. Basicamente lá é explicado os parâmetros a serem enviados e que o protocolo é o OAuth. Então detalho aqui como obter por exemplo os dados de um usuário apartir desta API.

Em primeiro lugar é preciso obter a consumer key e consumer secret de sua aplicação. Isso é feito gerando um token aqui: https://www.google.com/gadgets/directory/verify. Você deve colocar esse token dentro da tag content do xml descritor de sua aplicação e depois fazer a validação provando que é dono da aplicação. Com isso o Google irá lhe informar seu consumer key e consumer secret. Guarde eles com carinho.

Depois, com a gem oauth instalada você deverá executar um código semelhante ao exibido abaixo, com a premissa de que as variaveis consumer_key e consumer_secret estão preenchidas com os correspondentes à sua aplicação. E que a variável id é o id do usuário do Orkut que você está querendo conhecer melhor.

  consumer = OAuth::Consumer.new( 
consumer_key,
consumer_secret,
:site => 'http://www.orkut.com',
:scheme => :query_string,
:http_method => :get
)

request = consumer.create_signed_request(:get,
"/social/rest/people/#{id}/@self?xoauth_requestor_id=#{id}")
res = Net::HTTP.start('www.orkut.com', 80) do
|h| h.request(request)
end

puts res.body

sábado, 1 de agosto de 2009

TralhaController notificando observadores por pattern

TralhaController é uma biblioteca javascript que permite que aplicações web que utilizam requisições assíncronas ofereçam URLs para cada contexto de navegação do usuário. Para saber mais sobre ela basta ler o artigo Mantendo contexto usando ajax que o Bruno Carvalho escreveu recentemente.

Porém uma coisa me incomodava no uso dessa classe. Para cada observador registrado eu precisava dentro do método update verificar se ele precisava agir de acordo com a URL. Isso acaba por tornar o código mais confuso pois obrigava-nos a misturar validações e lógica de negócio. Veja um exemplo abaixo:

var observadorBusca = { update:function(url) {
  if( url.indexOf("#busca=")!=-1 ) {
    //Executa a busca
  }
} };
var observadorLink = { update:function(url) {
  if( url.indexOf("#link=")!=-1 ) {
    //Executa o link clicado
  }
} };
TralhaController.addObserver(observadorBusca);
TralhaController.addObserver(observadorLink);

Para tentar melhorar isso, pus a mão na massa e alterei o TralhaController permitindo registrar observadores que só fossem notificados diante de URLs que tivessem o padrão desejado. Utilizando então a última versão da biblioteca o código acima poderia então ser alterado para ficar da seguinte forma:

var observadorBusca = { update:function(url) { /* Executa a busca */ } };
var observadorLink = { update:function(url) { /* Executa o link clicado */ } };

TralhaController.addObserver("\\#busca\=.*", observadorBusca);
TralhaController.addObserver("\\#link\=.*", observadorLink);

Bem mais simples certo? É claro que há aqueles que não são muito fãs de expressões regulares e vão achar a primeira forma melhor. Para esses não há problema pois a forma antiga continua funcionando. Observadores registrados sem padrões continuam sendo notificados a toda e qualquer modificação de URL.

sexta-feira, 31 de julho de 2009

Roda: Eu também já recriei

Uma das discussões mais quentes do momento, e também uma das mais antigas, é sobre a mania que nós desenvolvedores temos de reinventar a roda. Eu mesmo já reinventei diversas vezes e hoje olho para trás e vejo quanto tempo poderia ter poupado ao usar alguma ferramenta já pronta, ou apenas ter adaptado uma que não se encaixava perfeitamente aos meus requisitos.

Há claro excessões, em que seu requisito é tão mínimo que reimplementar uma solução é mais rápido do que aprender uma já existente no mercado. Como foi o caso de um sistema que estávamos desenvolvendo em PHP e precisávamos de um framework apenas para separar a lógica da renderização de templates por tema. Validamos rapidamente diversos frameworks MVC e percebemos que era mais fácil criar em meia hora a seguinte classe:

class Renderizador {
function Renderizador($tema=null) {
$this->_dir = dirname(__FILE__) . "/templates/" . $this->tema;
}
public function put($nome,$obj) {
$this->$nome = $obj;
}
public function show($template) {
include( $this->_dir . "/" . $template . ".php");
}
}

A utilização dela era mais simples ainda. No exemplo abaixo as variáveis inseridas no renderizador ficam disponiveis para o template listagem.php através de $this, que nada mais é do que um arquivo php comum.

$renderizador = new Renderizador('cinza');
$renderizador->put('lista',[1,2,3,4]);
$renderizador->show('listagem');

Simples, sem precisar aprender nenhum framework ou linguagem de marcação nova. Em duas horas o time já estava trabalhando com a lógica separada da apresentação. Contudo essa é a exceção.

Em geral já recriei diversas rodas desnecessariamente desperdiçando muito tempo. Framework de mapeamento objeto relacional, CMS, blog e até mesmo o TimerTask do Java são alguns dos exemplos. Esse último me rendeu uma longa discussão com o Phillip Calçado na época em que ele trabalhava aqui. Felizmente ele foi persistente em me fazer desistir daquela implementação.

De qualquer forma, o importante é a lição aprendida. Hoje tento usar ao máximo ferramentas já disponíveis, e quando alguma não me atende perfeitamente, ao invés de começá-la do zero eu a altero e contribuo, como foi o caso de uma recente alteração que precisei fazer no fakeweb.

Outros membros da equipe também já vem de longe com esta mesma filosofia e estão constantemente contribuindo como Guilherme Cirne ao contribuir com o WillPaginate, o Anselmo também contribuindo com o Fakeweb e o Bruno Carvalho contribuindo com o TralhaController e o ExceptionNotifier.

O importante é aproveitar aquilo que a comunidade já lhe oferece pronto e testado. Economiza tempo seu, e do próximo desenvolvedor, que dará manutenção a algo que é padrão, com muita discussão em fóruns e muitas soluções documentadas.

sábado, 18 de julho de 2009

Dica rápida de charset para projetos rails com mysql

Se você está trabalhando em um projeto ruby on rails com banco mysql, e o charset definido tanto no servidor como no database.yml é UTF-8, tome o cuidado para que inserções no banco por conta de terceiros também seja feito neste charset.

Um simples insert feito na mão utilizando o cliente padrão do mysql pode te atrapalhar bastante. E o erro ficará bem difícil de encontrar pois este mesmo client exibirá o texto corretamente e só o rails entregará o dado incorreto, fazendo você acreditar que o problema está no ruby.

Para evitar isso, sempre que fizer inserts na mão e de massa de dados execute como primeira linha o seguinte comando:

charset utf8;