sexta-feira, 21 de dezembro de 2012

BDD (Behaviour Driven Development): O que é?

O que!!! Eu nem aprendi TDD e já inventaram mais uma técnica de desenvolvimento de software? Calma, na verdade BDD é TDD sobre uma nova nomenclatura.

Se é a mesma coisa, então por que estão reinventando a roda? Faz parte do ser humano tentar melhorar suas ferramentas e conceitos. Com isso, você pode pensar que BDD é melhor, mas como já disse, as duas técnicas permitem atingir o mesmo fim, sendo assim o que vai influenciar sua percepção a respeito de cada uma é sua experiência com testes. Se você já utiliza TDD e se sente confortável, provavelmente não vai perceber grandes diferenças no BDD.

No meu caso, BDD caiu como uma luva. Comecei a aprender TDD e ficava me pergutando se os testes que estava escrevendo faziam sentido e nem sempre conseguia responder isto de forma prática. Analisando este passdo agora, percebo que essa dificuldade surgia da falta de experiência com a técnica, o que me fazia perder o foco do que era essencial: o software em desenvolvimento.

Após ler um pouco sobre BDD, comecei a entender que a técnica é exatamente TDD, porém a utilização de uma nova nomenclatura tornou as coisas muito claras para mim, pois a técnica força com que você pense desde o princípio no comportamento que você espera da sua aplicação, assim fica muito mais fácil pensar se seu teste está fazendo a coisa certa ou não.

BDD força você a pensar no que seu software vai fazer através de três construções bastantes simples:  Dado um determinado contexto; Quando algum evento acontece; Então eu espero uma determinada saída. Esta é a tríade do BDD: Given, When e Then.

Estas três palavras mágicas me ajudaram muito a programar com testes e estou extremamente satisfeito com meu ritmo de desenvolvimento. Nos próximos posts irei falar de algumas ferramentas que dão suporte para a técnica e os benefícios que percebo de sua aplicação.

Até a próxima!

terça-feira, 2 de outubro de 2012

Enviar convites com Rails acessando a lista de contatos do usuário

Esta é uma tarefa extremamente simples de ser feita. A parte mais difícil é juntar todas as peças e colocar pra funcionar de um jeito eficiente.

Através da gem omnicontacts é possível recuperar a lista de contatos do GMail, do Yahoo! e do Hotmail, ou seja, os três principais webmails utilizados hoje em dia.

Eu configurei para recuperar contatos apenas do GMail, porém requisitar um acesso a API dos outros serviços é a parte mais simples. Para adquirir o consumer_id e o client_secret exigido pela omnicontacts acesse o link "API Access" do site Google APIs.

No site da omnicontacts já existem as informações de como configurar o gem para rodar com a sua aplicação e obter contatos. Algo bastante simples de se fazer.

O que achei mais complicado foi fazer a visão pro usuário, pois no meu caso, tenho quase 800 contatos no GMail, o que imediatamente me faz pensar em utilizar paginação. Pensei em utilizar o gem will_paginate, porém para guardar os itens selecionados pelo usuário teria que utilizar AJAX ou fazer alguma programação do lado do cliente. Além disso, teria que manter a lista de contatos recuperada na sessão, o que não é nenhum grande problema, mas acaba sendo algo a mais para gerenciar.

Foi então que me lembrei do DataTables, um ótimo componente em JQuery para criar paginação, ordenamento e pesquisa a partir de uma tabela. E o pessoal da comunidade Rails já criou até algumas coisas para facilitar seu uso e que estão muito bem explicados no RailsCasts #340!

Em um primeiro momento fiquei preocupado com a quantidade de dados que seria transferida para minha aplicação, mas os meus quase 800 contatos ficaram em aproximidadmente 45kb, ou seja, nada de assustar para os padrões de rede atuais.

A parte mais complicada desta solução é provavelmente o envio das informações para o servidor, que é feita através deste código (app/assets/javascript/contato.js.coffee):

$(document).ready ->
  $('form').submit ->
    elementos = $('input', oTable.fnGetNodes()).serializeArray()
    $(elementos).each (index, elemento) ->
      input = $("").attr("type", "hidden").attr("name", "emails[]").val(elemento.value)
      $('form').append($(input))  

Este código pega todos os itens marcados pelo usuário (linha 3) e para cada um deles cria um campo de input escondido com o e-mail e o adiciona ao formulário (linhas 5 e 6).

Criei uma aplicação para testar estas coisas, a única coisa que precisa ser alterada nela é o arquivo config/initializers/omnicontacts.rb para conter seu consumer_id e seu client_secret. Baixe aqui!

Happy hacking!

quinta-feira, 6 de setembro de 2012

Nested resources com mesmo nome e visões diferentes

Estou desenvolvendo uma aplicação em Rails e cheguei em um momente que tinha o seguinte problema: renderizar conteúdo diferente para 1 recurso aninhado de outros 2.

Vou utilizar um exemplo real para ilustrar melhor o caso. Digamos que estamos trabalhando numa aplicação parecida com o twitter e nossos 3 modelos são: usuários, postagens e tags. A partir delas eu posso ter as seguintes URLs na minha aplicação:
  • /usuario/1/postagens
  • /tags/1/postagens
Sendo necessário a seguinte configuração no meu arquivo routes.rb para obtê-las:

resources :usuarios do
  resources :postagens
end

resources :tags do
  resources :postagens
end

Sem nenhuma configuração adicional, isso irá fazer com que ambas as URL direcionem suas requisições para o mesmo controlador: PostagensController.

Para modificar este comportamento podemos utilizar namespaces. Para isso podemos criar uma pasta chamada usuario e outra chamada tag dentro do caminho /app/controllers e dentro dela colocar a classe PostagensController com o namespace correto, isto é o topo do arquivo teria que ficar da seguinte forma:
  • class Usuario::PostagensControllers < ApplicationController
  • class Tag::PostagensControllers < ApplicationController
Desta forma separamos o controlador de cada um dos nosso recursos aninhados, mas ainda precisamos instruir o Rails que ao receber uma requisição em um dos dois caminhos anteriores queremos que esta seja redirecionada para um controlador ou outro. Os guias na página do Rails tem uma parte ótima a este respeito: http://guides.rubyonrails.org/routing.html#controller-namespaces-and-routing

Para o meu caso modifiquei o arquivo routes.rb para o seguinte:

resources :usuarios do
  resources :postagens, :module => "usuario"
end

resources :tags do
  resources :postagens, :module => "tag"
end


Desta forma a URL não é alterada, apenas é feito o roteamento para o controlador no namespace correto.

O maior benefício desta estratégia é poder separar as visões para cada uma das requisições, sendo que o que é geral para as duas pode ser extraída para um template, enquanto os recursos específicos não ficam poluídos com ifs desnecessários e que tornam o entendimento da aplicação mais difícil, pois acaba adicionando detalhes de domínios distintos, podendo levar a distrações durante a leitura do código.

Os arquivos da visão ficariam então no seguinte caminho:
  • app/views/usuario/postagens/index.html.erb
  • app/views/tags/postagens/index.html.erb
A seção "Grouping Controllers into Modules" do livro Agile Web Development with Rails foi de grande ajuda para descobrir como implementar esta abordagem.
Maravilha, não!?!

domingo, 22 de janeiro de 2012

RSpec, factory_girl e transações com o banco de dados

Ontem estava quebrando a cabeça para entender como meus testes estavam rodando, pois para mim, a cada execução destes o meu banco de dados estava limpo. Isso é o que acontence quando se utiliza o Test::Unit, porém utilizando o RSpec é diferente.

Nada como uma boa noite de sono. Hoje pela manhã, com a cabeça fresca, fiz as buscas que realmente interessavam e a documentação está toda lá, no site do RSpec: https://www.relishapp.com/rspec/rspec-rails/docs/transactions

Além disso, quando se utiliza o FactoryGirl o comportando é diferente de quando se utiliza fixtures. Com fixtures, o banco é populado com o conteúdo declarado. Já com FactoryGirl o objeto é criado e salvo quando se faz a chamada Factory(:meu_modelo). Isso acabou me confundindo, pois no começo eu pensava que o FactoryGirl iria me retornar o que estava no banco, pois com uma sintaxe parecido é isso o que o fixtures faz.

O RSpec cria transações no banco para cada bloco it "..." do, sendo assim, caso algum objeto seja salvo fora deste escopo ele é mantido no banco e deve ser limpo manualmente.

segunda-feira, 9 de janeiro de 2012

O mundo é plano, porém ainda possui muitas ondulações

Já faz uns 5 meses que comprei um Kindle. Acho que deste que conheci o PC, há 20 anos atrás, não sentia tanta satisfação por possuir um aparelho eletrônico. Ele é um aparelho fantástico, agradável para ler, leve, possui um dicionário integrado, toca MP3, fácil para fazer anotações e marcar páginas. Possui todas as qualidades para substituir livros de papel (revistas e jornais também).

Apesar de tudo isso, acho que a penetração de tais aparelhos na sociedade ainda é muito pequena. Quantas pessoas que você conhece possuem um Kindle?

A Amazon fez um trabalho realmente incrível na produção deste aparelho e para incentivar os consumidores a adotar esta nova tecnologia, vendem abaixo do custo, por quê? Seu interesse é a venda de livros eletrônicos. Com a venda de poucos títulos é possível cobrir o custo de fabricação do aparelho e ter lucro.

Apesar disso, o governo brasileiro ainda não tomou nenhuma atitude para difundir este equipamento por aqui. Ao contrário, deu subsídios a Positivo na fabricação do Positivo Alfa, que apesar de possuir um dicionário em português, não apresenta nenhuma outra vantagem em relação ao Kindle. É mais lento, mais caro, a bateria dura menos, etc. Resumindo, não é um competidor.

Eu adoro as publicações da The Pragmatic Bookshelf e gosto muito também da qualidade do serviço deles. Quando você compra um livro para o Kindle, ele vai automaticamente para o seu aparelho e toda vez que o seu livro é atualizado com correções ou passa de uma publicação beta para a publicação oficial você recebe a atualização. Eles também mandam seus livros paraa sua conta do Dropbox. Definitivamente, um serviço excelente e muito cômodo!

Nos últimos tempos tenho lido rumores de que as editoras estão negociando para ter seus títulos no Kindle, porém não saberemos quando isso se tornará realidade nem como, mas já é uma luz no fim do túnel. Com isenção de impostos e bons acordos para oferta de conteúdo a experiência com a tinta eletrônica pode ser ainda mais incrível!