Closures em Javascript: o metodo each

Estou escutando uma discussão acalorada sobre Closures, Ruby e Javascript. Detalhes a parte, Javascript suporta funções anônimas (que não são closures) que podem ser passados como argumentos para outras funções.

Não seria otimo que um Array em javascript tivesse o método each para executar um bloco de código para cada elemento desse array? Usando o prototype da entidade Array eu posso adicionar um método each que faz isso:

Array.prototype.each = function(fun) {
	if (typeof fun != "function") return;
	for (var i=0; i<this.length; i++) {
		if(i in this) fun(this[i]);
	}
	return this;
}

var x = ["cat", "dog", "mouse", "bird"];
x.each(function(i) {
	alert(i);
});

Simples, não? Eu ainda posso adicionar outros métodos uteis como o map e o reduce.

São recursos interessantes que podemos utilizar quando for conveniente. Interessante foi descobrir a propriedade arguments, um array com todos os argumentos passados para a função que, se eu conhecesse alguns meses atrás, teria me solucionado alguns problemas :)

É claro que tudo isso ja vem no Prototype (né peleteiro).

Algoritmos Geneticos, Videos e VBScript

O Carlo “zED” Caputo ja tinha comentado sobre o uso de Algoritimo Genético para melhorar a qualidade dos vídeos aqui na globo.com, trabalho que rende bons frutos ao procurar profiles que combinem tamanho, qualidade e tempo de encoding. Ao trabalhar com profiles H.264 descobrimos uma coisa interessante: nem todos os profiles tocavam no iPhone. Não havia nenhuma regra aparente para gerar um profile “universal”, então algum tipo de teste mais “hard” deveria ser feito.

Manualmente, vc arrastava o video através do iTunes e o mesmo era aceito ou rejeitado pelo aparelho, tarefa que, se feita manualmente, é muito chata. Como automatizar isso?

Lembrei que muitos programas no mundo windows possuem uma interface COM que pode ser invocada por um vbscript ou mesmo C#. Isso me foi util uma vez quando praticamente reinventei um Selenium para integrar com uma ferramenta de testes muito ‘exotica’, dica retirada do livro .NET Test Automation Recipes: A Problem-Solution Approach. Nosso amigo google ajudou a localizar uma prova de conceito ao fazer a pesquisa “itunes vbscript”:

iCame, iPod, iScripted: Scripting iTunes

Era o que eu precisava. Talvez vcs estejam curiosos sobre vbscript ao inves de Jscript: é mais comum usar vbscript para esse fim pelo mundo afora, uma vez que COM é confundido com .com das urls pela busca do google.

Set objApp = CreateObject("iTunes.Application")
Wscript.Echo "Version: " & objApp.Version

Como podem ver, é muito simples. Existe um SDK desenvolvido pela Apple para trabalhar com o iTunes que é muito bem documentado e me ajudou muito. Bastava agora saber como adicionar os videos e capturar quando ocorre uma rejeição ou não.

Detalhes a parte, quando enfrentamos esse tipo de desafio precisamos pensar em termos de manutenção de codigo: como criar um script que tenha vida loga? Decidi usar as tecnologias mais comuns e ser o mais claro possivel (por isso vbscript, que não é tão tosco assim).

A integração com o Algoritmo Genetico foi feita usando um serviço assíncrono cuja integração era feita atraves do filesystem: bastava copiar um arquivo para um diretorio que um .bat do windows tomava conta do recado, disparando o vbscript. Foram necessarios algumas tentativas até chegar a uma estabilidade aceitável, pois acontecem coisas bem interessantes quando vc acessa uma aplicação via COM, principalmente quando um dispositivo esta ligado à USB e vc faz muitas vezes a mesma coisa. Como os serviços eram assincronos eu conseguia paralelizar parte das tarefas e, se precisasse escalar e testar em 2 ou mais aparelhos seria só desenvolver um sistema de lock apropriado (para 2 scripts não tentarem mover o mesmo arquivo).

Tirando o fato que eu tive q fazer coisas não-canônicas como usar o ping para localhost para simular um sleep (ok, considere isso como uma resposta ao select undef, undef, undef, 0.5;), foi divertido.

Esta estratégia pode ser usada em muitas coisas do mundo Windows, existem muitos exemplos pelo google afora e, se vc deseja algo mais profissional, basta usar o Visual Studio e programar em uma linguagem decente que compila e possui frameworks de teste unitários.

Trabalhando com Serviços Assíncronos

Geralmente desenvolvemos serviços síncronos, mesmo quando uma grande quantidade de etapas precisa ser executada, em detrimento de serviços assíncronos.

Imagine um sistema que, em dado momento, precisa enviar emails. Podem ser aqueles emails para convidar outras pessoas para usar a plataforma, algo que vc faz uns 50 ou 100 de uma vez. O desenvolvimento natural é ler essa lista de emails e, iterativamente, enviar um convite para cada pessoa (com algum codigo que identifique esse convite para evitar fraudes, por exemplo). Coisa que, dependendo do numero de emails e como cada passo é processado pode demorar um pouco.

Precisa ser síncrono? Vc precisa enviar esses convites naquele momento? Faz diferença se demorar uns minutinhos? Como esta solução sincrona afeta a performance do resto da minha aplicação (coisa que posso avaliar num teste de carga)? São perguntas que podemos nos fazer e dão algumas pistas, abrindo alas para a verdadeira pergunta: como a minha aplicação escala?

Se esta etapa pode demorar um tempinho e ela afeta (ou pode afetar) negativamente a experiência do usuario ao demorar para enviar 50 ou 100 convites, pode ser o momento de separar a minha aplicação do serviço de ‘convites por e-mail’, que ira trabalhar assincronamente sem concorrer tanto com CPU (ex: usando nice ou rodando em outro servidor). A minha aplicação apenas iria dizer “essas são as informações: nome do usuario e lista de emails para convites” que o sistema sabe se virar.

E como essa integração é feita? Existem varias formas. Eu posso fazer através do banco de dados, inserindo numa tabela que o meu sistema lê, porém integração pelo banco nunca é algo simples e frequentemente causa problemas (principalmente se a documentação se perde e altera alguma coisa no banco, prejudicando outras aplicações que ninguem sonha que trabalhem assim). Uma boa forma seria passar uma mensagem com todos os dados para o servidor de alguma forma inequivoca, como um POST HTTP (que retorna “ok, recebido, logo vou processar isso”) ou, simplesmente, escrevendo num arquivo em um determinado diretorio. O serviço trata de ler essa requisição e colocar em uma fila ou outra estrutura de dados para atender no prazo que for possivel.

No caso de uma lista de emails escrever em um arquivo parece grosseria mas se eu estivesse trabalhando com fotos, em um serviço de geração de thumbnails, por exemplo, parece ser uma saida bem simples. Nesse caso em particular eu posso criar um “vigia de diretorios”, alguem que vigia uma pasta de tempos em tempos e, quando surge algo novo, executa alguma ação propriamente dita. A forma como eu trabalho pode ser bem simples, como: acordar, listar fotos, executar ação, apagar fotos. Se eu quiser trabalhar com uma maquina de estados eu posso criar pastas como: chegada, processando, fim. O serviço trata de olhar a pasta de chegada e, quando precisa, move as fotos ou outras entidades para o diretorio processando e, então, executa alguma ação.

Nesse ponto algumas coisas precisam ser acertadas: se eu vou trabalhar com estados das minhas fotos, convites, ou qualquer outra entidade, posso modelar este serviço de forma que, se a minha entidade esta na pasta X, então ela tem um determinado estado. Isso é interessante pois caso o meu serviço seja interrompido basta mover tudo o que estava sendo processado para a pasta de chegada! Nesse ponto temos que ter bem claro o que acontece em cada transição de estado, os fluxos possiveis e estabelecer os corretos tratamento de erros.

Se tudo der certo, eu posso rumar para uma aplicação que, para escalar, bastaria subir um segundo serviço em outra maquina (ou aproveitando outra CPU). É claro que existem muitas outras formas de chegar a este resultado e muitas tecnologias disponiveis, porém soluções simples e robustas são sempre bem vindas: percebam que existem muitos pontos de falha que temos que tratar então com a suites de testes apropriada e uma documentação consisa podemos ter um bom serviço assincrono que só vem a agregar valor.

Inversão de Controle e Injeção de Dependências

Fui surpreendido por uma pergunta hoje: o que é IOC (inversion of control)?

Lembrei agora que um bom exemplo de IOC é programação orientada a eventos: lembram daquelas telas de Visual Basic que criavamos arrastando os componentes e adicionando codigo no “onClick” de um botão? Isso é uma forma de IOC pois não criamos o código que, dado o clique em um determinado componente, executa a ação, apenas jogamos um trecho e deixamos que a IDE “se virasse” para adicionar aquilo ao programa principal. Assim deixamos de controlar todo o fluxo para controlar uma parte dele.

Podemos pensar que IOC tem haver com “o que” e não, diretamente, “como”. Uma leitura interessante é o artigo do Martin Fowler intitulado Inversion Of Control – Containers de Inversão de Controle e o padrão Dependency Injection, com exemplos em Java.

Certificação para testes – vale a pena?

Sou suspeito para falar, aprendi a testar por necessidade, começando por um sistema muito complexo e cheio de bugs com documentação zero. Certificação pode valer a pena quando vc tem que seguir um processo bem burocratico, onde vc precisa de 1024 documentos e mais diversas ferramentas de apoio. Pessoalmente eu discordo dessa abordagem por um bom motivo: ruído.

Pode haver muito ruído entre o que o cliente quer (ou acha que quer) e todos os envolvidos, uma vez que nem sempre o cliente sabe o que quer exatamente ou pode não saber se expressa muito bem. Imagine agora isso colocando em uma coleção de documentos que circula entre os envolvidos e que exige um esforço considerável para manter-se atualizado. Pode ser a receita para o desastre :)

Uma condição interessante para o sucesso do projeto é a dedicação dos mesmos frente a honestidade. Muitos analistas, programadores ou testadores querem mesmo é tirar o seu da reta, uma vez que vivem sob um mundo medieval onde os chefes são temidos pelos urros guturais e ameaças perturbadoras, nesse caso o sentimento gostoso de “estou num projeto bom que produz um software de boa qualidade” passa a ser “caramba, se isso não funcionar não é culpa minha e sim do X”. Um bom exemplo é testadores que se limitam a seguir um manual de testes de forma exata e não fazem nada alem disso. Se um bug for encontrado, o testador se defende “mas eu segui o documento de testes #542″. Nesse caso ninguem precisa se defender: testar não elimina bugs, eles podem ser encontrados em situações muito peculiares e de dificil reprodução, nesse caso um bug deveria alimentar um documento de testes e não gerar pânico, dor e morte. Todo o software tem bugs, paciência.

Lembro de uma tela que poderia receber o campo “dia util do mês”. A tela funcionava quando eu informava 01, 02, 03, 10, 20, 28, etc. A tela parecia 100% dentro do uso normal até que alguem colocou o dia 08: um grande erro de javascript aparecia para o usuario! E com 09 também. Depois de muita pesquisa descobriram que, em javascript, um numero que começa com 0 é considerado octal e, em octal, não existe 8 nem 9, nesse caso a máscara desse campo era a origem desse comportamento estranho. Esse tipo de coisa só se resolve com trabalho, pois poderiamos ficar discutindo sobre quem errou, o testador ou o desenvolvedor, por horas, até acabar o projeto. O que importa é que 8 e 9 são dias úteis válidos e a funcionalidade ficaria comprometida com esse bug.

Alias, testar é uma atividade muito legal, se vc é curioso. Basicamente vc tem que procurar comportamentos erráticos e descobrir como reproduzi-los. Hoje em dia eu ainda me pego testando SQL Injection em alguns sites ou estudando paginação de dados, etc. Testar através de ferramentas é mais divertido ainda, não aqueles monstros que funcionam na base do Record/Playback: testar sites usando Shell Script (curl + grep), Selenium ou soluções próprias (como uma classe que se conecta com um webservice, um script Perl que envia dezenas de requests ou mesmo incorporar o Internet Explorer numa dll escrita em C# para integrar com algum sistema complexo de testes) é uma arte que traz benefícios como eliminar horas de trabalho braçal propenso a erros.

Não que alguem certificado em testes seria desonesto ou covarde mas a minha percepção é que esse universo de testes utiliza documentos, ferramentas e certificações também subterfúgios, tiro isso de conversas com gerentes dessa área que querem saber: quem é o desenvolvedor que produz mais bugs, quem demora mais pra desenvolver, quem demora mais pra testar, quem testa de verdade, etc. Quando vc pergunta específicamente essas coisas vc não quer métricas, vc quer dados para ferrar com alguem. Neste tipo de ambiente, tão comum ai por fora, a honestidade é soterrada pela mediocridade de gerentes e outros figurões e, geralmente, se resolve mudando de emprego ou fazendo parte de uma célula que implanta uma mudança de pensamento dentro da empresa. Geralmente chegamos a essa opção quando cansamos de trocar de emprego.

Por fim: conhecimento não pesa nem ocupa espaço, biológicamente falando. IMHO honestidade para o desenvolvedor, testador ou gerente é muito mais importante do que o cinismo. Experiência é mais importante que certificações. Conhecimento é mais importante do que um mero diploma. O resto é dançar conforme a música.

Eu gosto de citar este artigo: Introduzindo Agilidade num Ambiente, do Phillip Calçado, que me mostrou um novo mundo e me inspirou a sair do mundo de testes para o mundo onde se faz de tudo um pouco. Então, se alguem se perguntar se certificação, mesmo para testes, vale a pena, lembre-se que a sua postura profissional conta muito mais.