In my spare time (1h per day), I’ll try to study a new thing every day and after that, create a post about it in this blog. The “new” thing of the week is Redis. At the same time, I’ll practice my writing (and english) skills, so if something is unclear, just leave a comment. I’ll appreciate it.
To not forget why I’m spending this hour, I set some goals before I start. And they are:
Redis is a key-value store, also known as NoSQL database. Basically it stores data as ‘value’ inside a ‘key’. This data can later be retrieved only if we know the exact key used to store it.
I don’t want to detail the installation process, but if you’re using a Mac and Homebrew, just type brew install redis and that’s it!
The basic commands are SET and GET.
SET stores the data into a key. For example:
SET server:name "fido"
will store the value “fido” inside the “server:name” key and we can retrieve that key later with the GET command:
GET server:name => "fido"
If you try to retrieve a non existent key, Redis will return null:
GET server:ip => null
And the opposite of SET is DEL, that deletes the given key from the database.
Redis also has other cool stuff like Lists, Sets, Hashs and Ordered Sets. You can try this stuff online on http://try.redis-db.com (highly recommended) or check out the documentation online on http://redis.io/documentation.
Anything? I don’t think so, but there is collection of Redis use cases in this post. Some of them I like the most are URL Shortener Service and Who is online.
I’m still thinking on what will be my Redis use case. Anyway, I see a lot of projects using Redis to manage background tasks, but what I can conclude now is that Redis is fast!!!. Try to run redis-benchmark -q -n 100000 in the command line and you’ll see how fast it is.
And that’s it. The next post will be about Redis as well, probably about resque, or something related.
Lists are very useful. Take a look a this example:
$ mv README.txt README.markdown
A better way to do this using lists is:
$ mv README.{txt,markdown}This will do the same thing as the first example.
“!!” repeats the last command. This is useful in situations like this:
$ cp some_file.txt /some_directory_you_do_not_have_permission cp: some_file.txt: Permission denied
Aw crap! Instead of typing everything again or type combos like up-arrow + crtl-A + sudo, just type:
$ sudo !!
Bang-bang! Problem solved.
Another bang-like thing is the !$. This repeats the very last argument of the previous command. For example:
$ touch README.txt $ mate README.txt
Too much typing. Why not use !$ instead?
$ touch README.txt $ mate !$
Done.
And you, what are your terminal tricks?
I really don’t like the way we write normal Javascript confirmation messages, like this:
if ( confirm('Are you sure?') ) { // do the stuff }
So, I was wondering if this could be done in a more ‘functional’ way, like this:
confirmIf('Are you sure?', function() { // do the stuff });
Much better I think. Here is the function:
function confirmIf ( message, callback ) { if ( confirm(message) ) { return callback(); } return false; }
What do you think?
Estou iniciando meus estudos com desenvolivmento de jogos em Javascript e pretendo iniciar uma série de posts relacionados ao que aprendi durante esse tempo.
Para começar, veremos um pouco da API do canvas e de como ela pode nos ajudar a desenvolver jogos em Javascript.
Falando em jogos no browser, um elemento fundamental a se entender é o canvas. Ele que vai nos permitir desenhar objetos na tela e também movimentá-los, criar animações e etc…
Além do mais, ele é compatível com os browsers mais modernos (IE9, Firefox, Safari, Chrome), e para os mais antigos, existe uma biblioteca que contorna esse problema.
Para desenhar com o canvas, primeiro precisamos do elemento no nosso HTML, como esse aqui:
<!DOCTYPE html> <html> <head> <title>Canvas</title> </head> <body> <canvas id="canvas" width="960" height="480"> </canvas> <script type="text/javascript" charset="utf-8"> // Codigo aqui </script> </body> </html>
Feito isso, podemos manipular o canvas através do Javascript, veja esse exemplo:
var canvas = document.getElementById("canvas"); var paper = canvas.getContext("2d"); function draw() { paper.fillRect(10, 10, 100, 100); } draw();
Algumas coisas que temos que entender aqui. Primeiro, existe o elemento HTML do canvas na primeira linha. Depois, escolhemos um contexto para o nosso canvas (no caso 2d) com o getContext(). Esse contexto 2d que vai definir qual a API a ser utilizada nos nossos desenhos.
Um método dessa API é o fillRect, que basicamente desenha um retângulo na tela. Os dois primeiros parâmetros são a posição na tela, no caso X e Y, e os dois últimos são o tamanho do nosso retângulo (um quadrado, na verdade). Todos os parâmetros são em pixels.
Não vou entrar em detalhes de toda a API de desenho do canvas, mais informações você pode conferir aqui. Mas existe uma em especial que quero dar mais foco. Veja o exemplo abaixo:
function draw() { paper.beginPath(); paper.moveTo(10, 0); paper.lineTo(20, 30); paper.lineTo(0, 30); paper.closePath(); paper.stroke(); } draw();
No exemplo acima, estamos criando um desenho mais customizado. O método beginPath() diz ao canvas que vamos começar um desenho usando as APIs do PATH, que podemos associar a um “lápis” na tela.
O moveTo() move nosso “lápis” para o ponto 10,0 (x,y). A partir do ponto 10,0, desenhamos uma linha com o método lineTo() para o ponto 20,30. Neste momento, é como se fosse desenhada uma linha mesmo. O mesmo fazemos depois para o ponto 0,30. O método closePath() fecha automaticamente nosso desenho para o ponto inicial, no caso 10,0.
O método stroke() finaliza o desenho. Se você não chamá-lo ao final, seu desenho não aparecerá na tela. Isso porque o método lineTo() apenas adiciona as coordenadas numa fila, que depois são finalmente desenhadas com o stroke().
Para darmos mais vida ao nosso player, utilizaremos dois métodos do canvas: translate e rotate.
function draw() { paper.translate(30, 30); paper.rotate(0.1); //... }
O método translate() move o canvas inteiro para a posição X,Y passada nos argumentos. Já o rotate(), gira o canvas inteiro para o ângulo passado (em radianos). Essas duas chamadas dos métodos devem ser colocadas antes do beginPath(). Se colocarmos as chamadas depois do stroke(), nada vai acontecer, porque as transformações com translate() e rotate() só afetam os próximos desenhos.
Vamos trocar a chamada do draw() no fim do nosso script para isso:
setInterval(draw, 200);Agora você deve ter visto que nosso player se “move” na tela, mas não do jeito que esperávamos, porque o desenho antigo não é apagado da tela. Isso porque a cada 200 milisegundos estamos criando um novo desenho e não movendo o player de verdade. Para corrigir isso, chamamos o método clearRect(x, y, width, height) no início do método draw(). Esse método limpa todos os pixels dentro do retângulo passado.
O código final você pode ver abaixo:
var canvas = document.getElementById("canvas"); var paper = canvas.getContext("2d"); function draw() { paper.clearRect(0, 0, 960, 480); paper.translate(30, 30); paper.rotate(0.1); paper.beginPath(); paper.moveTo(10, 0); paper.lineTo(20, 30); paper.lineTo(0, 30); paper.closePath(); paper.stroke(); } setInterval(draw, 200);
Essa ainda não é a forma ideal de movermos nosso player na tela, principalmente porque estamos movendo o canvas inteiro e não só o player. Se tivermos outros desenhos na tela, você vai ver que eles se moverão junto com o nosso player, e isso não é o ideal. Falarei mais sobre este problema e como evitá-lo no próximo post da série.
Até aqui conseguimos entender um pouco do canvas, e de como sua API nos permite fazer coisas bem interessantes. Também já conseguimos ter uma boa idéia de como vai ser nosso jogo e de como podemos estruturá-lo.
No próximo post continuaremos nossa série, falando mais sobre a estrutura do nosso jogo e de como manipular nosso player com o teclado.
Até lá!
O contenteditable é uma das novas funcionalidades do HTML5. Ela permite que o usuário consiga editar o conteúdo do elemento, como se fosse um daquels editores WYSIWYG. Para utilizar, basta adicionar o atributo contenteditable="true" no elemento:
<ul contenteditable="true"> <li>Item 1</li> <li>Item 2</li> </ul>
Neste exemplo, ao clicar na lista, aparecerá algo parecido com uma textarea, tornando o conteúdo editável, como se fosse um editor de textos.
Como saber o momento que o usuário terminou de editar o texto dentro do nosso conteúdo editável? Simples. Como o elemento se transforma em uma “textarea”, podemos utilizar os mesmos eventos de um input padrão do HTML. Veja este exemplo:
<!DOCTYPE html> <html> <head> <title>contenteditable</title> <style type="text/css" media="screen"> *[contenteditable]:hover { background-color: #F9F9F9; } </style> </head> <body> <div contenteditable="true">Edit me!</div> <ul contenteditable="true"> <li>Item 1</li> <li>Item 2</li> </ul> <div id="log"></div> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script> <script type="text/javascript" charset="utf-8"> $(function() { $("[contenteditable]").bind('focus', function() { $(this).data('original-text', $(this).text()); log(this, 'focus'); }); $("[contenteditable]").bind('blur', function() { log(this, 'texto alterado de "' + $(this).data('original-text') + '" para "' + $(this).text()); }); }); function log (el, msg) { console.log(el, msg); $("#log").append("-> " + msg + "<br/>"); } </script> </body> </html>
Ao clicar na div com o atributo contenteditable por exemplo, é disparado o evento onfocus. Se logo após clicarmos em outra área do HTML (fora da nossa div), é disparado o evento onblur. Isso você pode acompanhar no log logo abaixo.
No Javascript logo no fim do arquivo, estamos adicionando eventos (com jQuery) a todos os elementos que tiverem o atributo contenteditable. Os eventos são focus e blur, respectivamente relacionados aos eventos nativos do Javascript onfocus e onblur. Um outro exemplo pode ser visto aqui.
Um curiosidade é que esta funcionalidade existe desde o IE 5.5, inclusive foi a Microsoft que padronizou e implementou esta funcionalidade. Mas como a documentação era apenas superficial, só agora esta funcionalidade está sendo padronizada pelo W3C.
Outro detalhe é que é possível executar coisas como execCommand dentro de elementos com contenteditable. Essass funções são utilizadas pelos editores WYSIWYG, para adicionar negrito, itálico, etc… essas coisas.
Mais detalhes sobre o atributo podem ser conferidos neste post.
Update: O Juan Maiz deu uma dica legal de um editor WYSIWYG totalmente em HTML5, segue o link: http://aloha-editor.com/
Existem duas formas de se criar uma Struct em Ruby. Uma delas é simplesmente atribuindo a uma constante:
Color = Struct.new(:color, :hex) do def metodo end end
E a outra seria com herança:
class ComplexColor < Struct.new(:color, :hex) def metodo end end
Notei que muita gente ou fazia de um jeito, ou fazia de outro, e fui atrás pra ver se realmente tinha alguma diferença, ou se era apenas uma questão de “sintax sugar”. E tem diferença!
A diferença principal é que, quando criamos uma struct com herança, estamos colocando uma struct anônima na hierarquia de ancestrais do nosso objeto:
>> Color.ancestors => [Color, Struct, Enumerable, Object, Kernel] # herança normal >> ComplexColor.ancestors => [ComplexColor, #<Class:0x100cb7838>, Struct, Enumerable, Object, Kernel] # ahá!
E isso faz também com que os membros (métodos getters e setters) da struct fiquem na classe pai:
>> Color.instance_methods(false) => ["color", "hex", "color=", "hex=", "description"] # normal >> ComplexColor.instance_methods(false) => ["description"] # ops? >> ComplexColor.superclass.instance_methods(false) => ["color", "hex", "color=", "hex="] # ahá!
Claro que isso não faz muita diferença, afinal ao chamar qualquer método dos membros da struct, o Ruby vai subir um nível na hierarquia se ele não achar o método na classe atual.
Agora se você gosta de fazer micro-otimizações, talvez isso faça algum sentido, já que estamos economizando alguns pentelhésimos de segundo nas execuções sem herança.
Recentemente tive que atualizar a versão do meu zsh com o homebrew, e não foi tão fácil como eu imaginei. Isso porque a versão instalada pelo homebrew, não é instalada no diretório padrão /bin.
Encontrei a solução e fiz um tutorial rápido, caso você também encontre esse problema.
brew install zsh
Depois de tudo compilado e instalado (thanks homebrew), você terá que alterar o shell padrão para usar o novo zsh com o comando:
chsh -s /usr/local/bin/zsh
Neste momento aparece a mensagem “chsh: /usr/local/bin/zsh: non-standard shell”. Isso acontece porque a versão do homebrew não está habilitada para ser usada como shell. Para habilitar, adicione a seguinte linha no fim do arquivo /etc/shells:
# /etc/shells /usr/local/bin/zsh
Feito isso, altere o shell padrão com:
chsh -s /usr/local/bin/zsh
Feche e abra novamente o terminal e o zsh está instalado.
Update: Parece que a gem Saikuro original não funciona bem com a metric_fu, por isso é necessário instalar o fork do devver e também atualizar a configuração do metric_fu na linha 10. Confira logo abaixo.
Update2: Atualizado para a versão 1.4.0 da gem metric_fu.
metric_fu é uma gem que serve pra analisar o código da sua aplicação. Ela faz a análise utilizando diversas métricas conhecidas, como por exemplo, cobertura de testes, complexidade ciclomática, code smells e etc…
Essa gem é um conjunto de outras gems, em que cada uma delas é responsável por um tipo de métrica. O que a metric_fu faz, é agrupar todas em um lugar só, provendo um html único para acessar as métricas geradas.
Para instalar a gem e suas dependências, basta rodar o comando gem install.
gem install metric_fu => Successfully installed metric_fu-1.4.0 => 1 gem installed
Depois de alguns testes, descobri que a gem Saikuro original não funciona bem com o metric_fu (sequer rodou as análises do código), para corrigir isso, instale o fork do devver (link no github):
gem install devver-Saikuro => Successfully installed devver-Saikuro-1.2.0
Na versão 1.4, foi adicionado suporte a gem rails_best_practices, que verifica a qualidade do código da sua aplicação de acordo com as melhores práticas. Mais informações nessa apresentação.
gem install rails_best_practices
=> Successfully installed rails_best_practicesA metric_fu utiliza o Rake para gerar as métricas e por isso, recomendo criar o arquivo metric_fu.rakefile dentro de lib/tasks com o seguinte conteúdo (gist aqui):
require 'metric_fu' MetricFu::Configuration.run do |config| config.metrics = [:churn, :saikuro, :stats, :flog, :flay, :reek, :roodi, :rcov, :rails_best_practices] config.graphs = [:flog, :flay, :reek, :roodi, :rcov, :stats, :rails_best_practices] config.flay = { :dirs_to_flay => ['app', 'lib', 'spec'], :minimum_score => 10, :filetypes => ['rb'] } config.flog = { :dirs_to_flog => ['app', 'lib'] } config.reek = { :dirs_to_reek => ['app', 'lib'] } config.roodi = { :dirs_to_roodi => ['app', 'lib'] } config.saikuro = { :output_directory => 'tmp/metric_fu/scratch/saikuro', :input_directory => ['app', 'lib'], :cyclo => "", :filter_cyclo => "0", :warn_cyclo => "5", :error_cyclo => "7", :formater => "text" } config.churn = { :start_date => "1 year ago", :minimum_churn_count => 10 } config.rcov = { :environment => 'test', :test_files => ['test/**/*_test.rb', 'spec/**/*_spec.rb'], :rcov_opts => ["--sort coverage", "--no-html", "--text-coverage", "--no-color", "--profile", "--rails", "-Itest:spec", "--exclude /gems/,/Library/,spec"], :external => nil } config.graph_engine = :bluff end
Feito isso, basta rodar o comando rake metrics:all.
rake metrics:allO comando Rake irá abrir automaticamete o browser depois de rodar as métricas, e você pode encontrá-las dentro do diretório temp/metric_fu/output/index.html.
Vale ressaltar que as configurações estão utilizando o Rspec e o Test::Unit. Caso não existam algum deles, basta remover do config.rcov (linhas 20 e 27) as referências ao framework de testes que você não está utilizando. Mais configurações podem ser encontradas aqui.
Pelo que notei, as métricas só são armazenadas uma vez por dia, não tenho certeza se isso é configurável (se você conhece alguma configuração que mude isso, por favor poste nos comentários).
Vale a pena dar uma olhada nas gems que a metric_fu engloba (Saikuro, Flog, Flay, Rcov, Reek, Roodi, Churn e rails_best_practices) para entender mais o que é cada métrica e se quiser, rodá-las em separado.
Métricas geralmente são boas, mas não se deve usá-las como regra, apenas como referência. Muitas métricas podem acabar não sendo totalmente verdade (como 100% de cobertura de testes, por exemplo), por isso devem ser usadas apenas como um aviso.
Para mais informações sobre a gem, acesse o site aqui.
Já me peguei várias vezes fazendo algo parecido com isso:
$('td').removeClass('normal'); $('td').addClass('selecionado');
Então, para facilitar, criei um plugin:
jQuery.fn.extend({
replaceClass: function(original, replace) {
return this.each(function() {
jQuery(this).removeClass(original);
jQuery(this).addClass(replace);
});
}
});Agora consigo fazer:
$('td').replaceClass('normal', 'selecionado');
Espero que sejá útil.
O jQuery possui algumas formas para iterar sobre coleções (arrays, objetos e elementos) utilizando os métodos $.each, $.map e $.grep. Esses métodos são bem parecidos, porém cada um tem um uso específico.
$.eachO método $.each serve para passar (iterar) por cada elemento de um objeto e fazer uma determinada ação. Essa ação é feita com um callback (função anônima), que é chamada em cada elemento. O $.each espera dois parâmetros: o primeiro é o objeto e o segundo é o callback, este que, fornece dois parâmetros, o primeiro é o índice (se for um array) ou propriedade (se for objeto), e o segundo o próprio elemento:
// Iterando em um array
$.each(['a','b','c'], function(index, element) {
console.log(index, element);
});
=> 0, 'a'
=> 1, 'b'
=> 2, 'c'
// Da mesma forma com objetos
$.each({ 'nome': 'Edward Hill', 'musica': 'Trolololo' }, function(key, value) {
console.log(key, value);
});
=> 'nome', 'Edward Hill'
=> 'musica', 'Trolololo'Duas dicas: para parar em qualquer momento a execução do $.each, o callback deve retornar false, já se for preciso apenas "pular" um elemento e continuar a execução, o callback deve retornar true:
// Para a execução se encontrou um 'b' no array
$.each(['a','b','c'], function(index, element) {
if(element === 'b') {
return false;
}
console.log(element)
});
=> 'a'
// Já aqui, ele 'pula' a execução se encontrou um 'b', e continua para os outros elementos do array
$.each(['a','b','c'], function(index, element) {
if(element === 'b') {
return true; // observe o true aqui
}
console.log(element)
});
=> 'a'
=> 'c'Uma forma de usar o $.each (talvez a mais utilizada), é com elementos do DOM, e aqui que a coisa fica mais interessante. A estrutura e o funcionamento são os mesmos, a diferença é que o $.each é executado a partir de uma coleção de elementos do jQuery:
// Itera sobre todos os elementos forms encontrados
$('form').each(function(index, element) {
console.log(this);
};
=> <form ...>
=> <form ...>
=> <form ...>$.mapO método $.map tem um objetivo diferente do $.each. Ele também executa um callback para cada elemento, porém só retorna o que for especificado no retorno. Se não for preciso retornar alguma coisa, o callback deve retornar null:
// Retorna somente os elementos pares
$.map([4, 5, 6], function(element, index) {
return element + 4;
});
=> [8, 9, 10]
// Retorna somente os elementos pares. Funciona como um 'filtro' no array
$.map([4, 5, 6], function(element, index) {
return element % 2 === 0 ? element : null;
});
=> [4, 6]Assim como o $.each, o $.map pode ser usado com elementos do DOM. Aqui as possibilidades são infinitas, vai da criatividade de cada programador. Algumas formas de uso:
// Cria um array com todos os atributos "method" dos formulários
$('form').map(function() {
return $(this).attr("method");
});
=> ["get", "post", "post", "get"]
// Retorna todos os forms que o método form igual a GET
$('form').map(function() {
return $(this).attr("method").toLowerCase() === "get" ? this : null;
});
=> [<form method="get">, <form method="get">]Resumindo, qualquer coisa diferente de null, será retornada pelo método $.map.
$.grepO método $.grep é praticamente um $.map, porém mais específico. A idéia do $.grep é utilizar o callback como um filtro para o array, mais ou menos como o $.map faz, porém tem duas diferenças: a) só funciona com arrays; b) só retorna os elementos que passarem em uma determinada condição. Em outras palavaras, o callback deve retornar true ou false, e se o retorno for true, o elemento do array é retornado:
$.grep([1, 2, 3, 4], function(elemento) {
return elemento > 2;
});
=> [3, 4]Uma forma de fazer isso com elementos do DOM é com o método filter do jQuery:
// HTML
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
// o filter funciona como um $.grep, só que para elementos do DOM
$('li').filter(function() {
return $(this).text() === "Item 2";
}).addClass('red');
// HTML depois
<ul>
<li>Item 1</li>
<li class="red">Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>Para não se confundir em quando usar cada método, tenha em mente isso:
$.each executa um callback em cada elemento (array, objeto, ou elemento do DOM), e sempre retornará todos os elementos;$.map funciona da mesma forma que o $.each, com a diferença que você pode especificar o que será retornado;$.grep só funciona com arrays, e para retornar um elemento do array, o callback deve retornar true;$.grep, mas como elementos do DOM, o jQuery disponibiliza o método filter;Documentação
Já faz um tempo que venho procurando uma biblioteca para testes unitários em Javascript que seja fácil de utilizar. Uns posts atrás, falei um pouco sobre a JsUnitTest, que você pode conferir aqui e aqui.
Neste post pretendo mostrar um pouco da biblioteca QUnit.
De acordo com o site do projeto: "QUnit é uma suite de testes em Javascript, poderosa e simples de usar". Está sendo utilizada principalmente para testar o projeto jQuery e alguns plugins, mas é capaz de testar qualquer código Javascript genérico.
QUnit é realmente simples de usar. Você precisa basciamente incluir os arquivos qunit.js e qunit.css e ter uma estrutura básica de HTML para que a biblioteca funcione corretamente. Um exemplo pode ser conferido aqui.
Ao abrir este exemplo, você deve ver uma tela parecida com essa:
Os blocos de teste são feitos com o método test que espera 2 parâmetros: o primeiro com o nome do teste e o segundo com uma função contendo as asserções:
test("um teste simples", function() {
ok(true, "tem que passar");
});O que resulta isso:
O método ok, verifica se o primeiro parâmetro é true, similar ao assert da JsUnitTest. Se trocarmos para false o primeiro parâmetro do ok, veremos essa tela:
Dica: Para ver qual asserção falhou dentro do teste, é só clicar no nome do teste.
Conhecendo o ambiente básico, podemos passar pra algo mais específico.
Veja o exemplo abaixo:
module("modulo simples", {
// executa um código específico antes de cada teste
setup: function() {
this.setup_var = true;
},
// executa um código específico depois de cada teste
teardown: function() {}
});
test("um teste com ok", function() {
ok(this.setup_var, "testa a variavel criada no setup");
});
test("um teste com equals", function() {
equals($("h1").length, 1, "deve ter um elemento h1");
equals($("h2").length, 2, "deve ter dois elementos h2");
});
module("outro modulo");
test("um teste com same", function() {
same([1, 2, 3], [1, 2], "same verifica todas as propriedades ou elementos recursivamente");
});O método module serve como um "agrupador" de testes, separando os testes em módulos (óbvio, dãã). É possível passar um parâmetro adicional com as funções setup e teardown. A função setup é executada antes de cada teste, já a teardown é executada depois de cada teste.
Existem outros métodos de asserções que são o equals(atual, esperado, mensagem) e o same(atual, esperado, mensagem). Basicamente eles testam se o primeiro parâmetro é igual ao segundo, com a diferença que o same é mais preciso, e testa todas as prorpiedades dos parâmetros passados recursivamente, o que é útil principalmente com arrays e objetos.
A biblioteca QUnit se mostrou muito simples de usar nos testes que fiz, cumprindo sua promessa. Recomendo para quem quer iniciar algo com testes unitários em Javascript, principalmente por ela ser muito fácil de usar e quem já está habituado com o mundo do TDD não vai ter nenhuma dificuldade em se adaptar.
Você pode conferir a documentação mais detalhada no site do jQuery, ou pode acompanhar este blog, que em breve estarei postando mais artigos relacionados ao QUnit e testes em geral.
Este post é uma tradução livre do post "Separate Estimating from Committing" de Mike Cohn.
Um problema fundamental e comum em muitas organizações é que as estimativas e os compromissos são considerados equivalentes. A equipe de desenvolvimento (ágil ou não) estima que a entrega de um determinado conjunto de funcionalidades levará sete meses com os recursos disponíveis. Os membros da equipe informam essa estimativa para o seu gerente, que passa a estimativa ao vice-presidente, que então informa o cliente. E, em alguns casos, a estimativa é "podada" ao longo do caminho para prover à equipe uma "meta elástica".
O problema aqui não é que a estimativa da equipe de sete meses esteja certa ou errada. O problema é que a estimativa foi transformada em um compromisso. "Estimamos que isso levará sete meses", foi traduzido para "Nos comprometemos a terminar em sete meses". Estimar e se comprometer são ambos importantes, mas devem ser vistos como atividades separadas.
Precisei buscar minha filha na natação esta noite. Perguntei a ela em que horas ela estaria pronta (o que nós definimos como: término da natação, tomar banho, e estar pronta para ir para casa). Ela disse, "Eu devo estar pronta pelas 5:15". Essa era a sua estimativa. Se eu tivesse pedido para ela se comprometer - estar fora das instalações até aquela hora ou eu iria embora - ela poderia ter se comprometido para as 5:25, para dar tempo de resolver eventuais problemas, como uma prática um pouco mais longa, o relógio do treinador estar atrasado em cinco minutos, fila nos chuveiros, e assim por diante. Para determinar um tempo no qual ela poderia se comprometer, ainda assim ela teria feito uma estimativa. Mas ao invés de me dizer sua estimativa diretamente, ela teria transformado-a em um prazo no qual poderia comprometer-se.
Não deixe que suas estimativas se tornem compromissos. Lembre-se da diferença entre uma estimativa e um compromisso e mantenha as duas atividades separadas, instruindo os gerentes e os clientes, se necessário. Eu falo muito mais sobre estimativa ágil e compromisso no meu novo livro, "Succeeding with Agile".
Este post é uma tradução livre do artigo "Step one is admitting you have a problem" de David Heinemeier Hansson
O mundo das startups está repleto de pessoas viciadas em trabalho e esse vício muitas vezes tem um preço caro: amizades perdidas, relacionamentos acabados, saúde ruim, e falta de interesse em outros assuntos. Tudo o que importa é a próxima tarefa de trabalho, o próximo negócio, a próxima etapa, a próxima rodada de financiamento.
Se você tivesse um vício semelhante a cocaína ou o álcool, as pessoas iriam chamá-lo de doente e aconselhá-lo a procurar ajuda. Mas no mundo das startups, esse vício é elogiado por muitos. Você é considerado um herói por apostar todas as suas fichas da vida na esperança de que você vai acertar um royal flush.O pior é que a maioria desses viciados sabem conscientemente que a jornada de 14 horas de trabalho não é realmente uma forma muito produtiva de seguir em frente. Que mais tempo não significa trabalho mais valioso. Jason Cohen aborda este assunto em: "Sacrifice your health for your startup". Ele reconhece que a privação do sono não é muito útil, mas ainda vê como um distintivo de honra, que o trabalho extra provavelmente não faz produto de qualidade, mas de certa forma ainda precisa existir.Ele fala com a voz da razão, mas age como um viciado. Desesperado para encontrar uma justificativa para os seus caminhos, ele diz: Você não precisa ser nada além de trabalho porque você já tem que fazer muitos papéis. Você precisa ter uma obsessão única com o trabalho porque o nirvana da "liberdade financeira" está a apenas alguns passos à frente.Ser viciado em seu trabalho pode ser um pouco melhor do que um vício em cocaína, mas segue o mesmo padrão de abuso e de escapismo. E o mais importante, não é um requisito para o sucesso. Você não precisa se tornar um viciado para abrir uma startup. Apenas seja apaixonado, obcecado, mas não deixe que isto seja uma desculpa para consumir a sua vida.
A biblioteca JsUnitTest, que mostrei no post anterior, oferece uma forma simples para testar funções que são executadas de modo assíncrono - como setInterval e setTimeout - através do método wait
wait(tempo_em_milisegundos, bloco_de_codigo);
O método wait espera o tempo determinado em milisegundos e executa o bloco de código, que nada mais é do que uma função anônima. Um exemplo de teste:
testAsync: function() { with(this) { var executou = false; var timeout = setTimeout(function() { executou = true; }, 10); wait(100, function() { assert(executou); });}}O teste é simples, depois de 10 milisegundos a variável executou recebe true. A asserção que ficou dentro do método wait só é executada depois de 100 milisegundos, um tempo um pouco maior, apenas para dar uma segurança.
Na busca por uma soulção simples para Test-driven Development em Javascript, me deparei com o projeto JsUnitTest ou JavaScript Unit Testing framework.
O projeto foi criado pelo Dr. Nic e é extremamente simples e muito útil. Mais detalhes podem ser vistos aqui.
Faça o download do projeto em tar ou zip e descompacte. Podemos ver três arquivos:
assets/jsunittest.js assets/unittest.css example_test.html
Logo depois, abra o arquivo example_test.html no browser para checar se está tudo ok.
Agora é só linkar o arquivo da sua biblioteca Javascript e sair testando.
No exemplo podemos ver o método testTruth com uma asserção e aí entra um detalhe importante: apenas os métodos que iniciem com test serão executados pelo framework.
Podemos criar mais um teste logo abaixo:
testColor: function() { with(this) {
assertEqual('Green', Mushroom.color)
}}Obviamente este teste irá falhar, então vamos criar um objeto rapidamente só para passar nos testes:
var Mushroom = {
color: 'Green'
}Agora os testes passaram e você pode tomar seu chá de cogumelo!
Bom, essa é apenas uma introdução. Pretendo cobrir mais pontos interessantes desse framework de testes nos próximos posts.
Digamos que você precise executar uma determinada função a cada 2 segundos, ou algo que precise ser repetido de tempos em tempos, como por exemplo um timer.
Em Javascript isso é possível através das funções setInterval e clearInterval, e seu uso é bem simples, veja:
var hello = setInterval(function() {
console.log('Hello world!');
}, 2000);
// a cada 2 segundos (tempo em milisegundos), escreve no console 'Hello world!'O primeiro parâmetro da função setInterval recebe uma função anônima que será executada de tempos em tempos, e o tempo entre cada execução é dado pelo segundo parâmetro. Este tempo está em milisegundos.
Note que atribuímos o retorno da função setInterval à variável hello, por que isso? Porque a função setInterval retorna um identificador - que é um número qualquer - e este número é usado na função clearInterval, para interromper o timer associado.
// para a execução clearInterval(hello);
A função clearInterval interrompe o timer associado á variavel passada. Somente isso.
Podemos reaproveitar essa função, passando-a por referência:
function hello() {
console.log('Hello world!');
};
var intervalo = setInterval(hello, 2000);
var intervalo_longo = setInterval(hello, 5000);
// ...
clearInterval(intervalo);
clearInterval(intervalo_longo);Caso essa função precise de parâmetros, podemos passá-los logo após os dois primeiros:
function mensagem(msg) {
console.log(msg);
}
setInterval(mensagem, 2000, 'Hello World!');
setInterval(mensagem, 2000, 'Hello Moon?');Ou seja, todos os parâmetros após os dois primeiros, serão passados para função mensagem.
Nunca faça algo assim:
setInterval("alert('Hello World!')", 1000);Passando uma string para a função setInterval, tem o mesmo comportamento que o eval, e como sabemos, ‘Eval is Evil’.
Também tome cuidado ao criar muitos timers, se houverem muitos simultâneos, o browser simplesmente trava!
Quer acessar a documentação (rdoc) das gems instaladas facilmente? Simples, vá ao terminal e digite:
~$ gem server => Starting gem server on http://localhost:8808/
Pronto, você acabou de subir um servidor de gems na sua máquina! Acessando a url http://localhost:8808/ e você verá a documentação de todas as gems instaladas na sua máquina.
E não é só isso, outras pessoas podem baixar e instalar gems que estão na sua máquina, simplesmente digitando:
~$ gem install SUA_INCRIVEL_GEM -s http://SUA_MAQUINA:8808
Eu particularmente não gosto muito do tema padrão do RDoc, e se você concorda comigo, siga os seguintes passos:
A gem hanna cria um tema diferente para a documentação das suas gems, e sua instalação é simples:
~$ sudo gem install hanna => Successfully installed hanna-0.1.12
Para trocar o template padrão ao instalar uma gem (comando gem install), edite o arquivo .gemrc:
rdoc: --inline-source --line-numbers --format=html --template=hanna
Para atualizar os arquivos de documentação com o novo template, digite no terminal:
~$ sudo gem rdoc --all --no-ri
Isto irá gerar todos os arquivos de documentação das gems novamente (pode demorar um pouco).
Agora se você subir o servidor de gems novamente, verá que o tema ficou muito mais interessante! (exceto pela primeira página que ainda tem o tema antigo).
Mesmo assim ainda é muito chato ter que entrar no terminal o tempo todo e subir o servidor de gems. Pensando nisso, Jason Seifer postou uma forma muito legal de acessar o RDoc das suas gems nesse tutorial (alguns passos já foram descritos aqui).