Ruby e Rails no Mundo Real 2010

15, março, 2010

Estão abertas as inscrições para o Ruby e Rails no Mundo Real 2010, o evento da comunidade de Ruby e Rails em São Paulo.

O evento, que é organizado pelo Guru-SP, acontecerá no dia 29 de maio, das 9h às 18h, no Century Flat Paulista, Rua Teixeira da Silva, 647 - Paraíso.

A edição do ano passado reuniu 160 participantes, e eu estava lá. Dá uma olhada no cara de camiseta laranja e boné vermelho na foto.

Esse ano as palestras serão:

O valor da inscrição é R$ 69,00 ou R$ 49,00 para quem participou do ano passado.

Demais informações você tem no site do evento.

Eventos, Ruby , , , ,

Algoritmo para encontrar o range ao qual pertence um número

9, março, 2010

Para atender uma lógica de negócio tivemos a necessidade descobrir qual range um determinado número pertencia.

Por exemplo, para intervalos de 5000, todos os números de 1 a 5000 (inclusive) pertecerão ao range 5000, os números 5001 a 10000, pertencerão ao range 10000, de 10001 a 15000, ao range de 15000 e assim por diante.

Então tínhamos que criar um algoritmo que recebendo a posição e o intervalo, devolveria o range ao qual a posição se encontrava.

def generate_range(position, interval)
  # Implementation
end

generate_range  4999, 5000 # =>  5000
generate_range  5000, 5000 # =>  5000
generate_range  5001, 5000 # => 10000
generate_range 12350, 5000 # => 10000

Eu dei a idéia de substrair do valor da posíção o valor do intervalo até o valor da posição ficar negativo e então multipiclar a quantidade de vezes que aconteceu a subtração pelo valor do intervalo.

A primeira versão saiu algo do tipo assim:

def generate_range(position, interval)
  rest = position - interval
  substract = 1

  if rest > 0
    substract += 1

    while rest > 0
      rest = rest - interval
      substract += 1 if rest > 0
    end
  end

  substract * interval
end

Zuado esse código, né?

Nisso o Herbert Francarelli perguntou para o Fábio Akita se havia alguma forma em Ruby para diminuir a quantidade de código desse método. De bate pronto ele pensou em usar o método include? da classe Range, mas queria uma solução de uma linha.

Ficamos de cada um da equipe tentar encontrar a solução do problema no fim de semana.

Eu comecei tentando utilizar o método include? da classe Range:

def generate_range(position, interval)
  lrange = 1
  rrange = interval

  while !(lrange..rrange).include?(position) do
    lrange += interval
    rrange += interval
  end

  rrange
end

E também consegui resultado semelhante com a ajuda do método between? da módulo Comparable:

def generate_range(position, interval)
  lrange = 1
  rrange = interval

  while !position.between?(lrange, rrange) do
    lrange += interval
    rrange += interval
  end

  rrange
end

Mas somente quando eu parei de procurar alguma solução com comandos Ruby e pensei em solução matemática, encontrei o que eu queria:

def generate_range(position, interval)
  ((position - 1) / interval + 1) * interval
end

O Fábio Akita já tinha criado o método com uma linha no mesmo dia. Ele combinou a solução matemática com recursos da linguagem:

def generate_range(position, interval)
  (position.to_f / interval.to_f).ceil * interval
end

Essas coisas (entre muitas outras) é que tornam divertido o trabalho de desenvolvedores de software.

Ruby , ,

Instalando ImageMagick no Mac OS X 10.5

4, março, 2010

PaperClip é um plugin de upload de arquivos para Ruby on Rails que cria atributos dos arquivos nas classes ActiveRecord funcionando da mesma maneira como se estivesse utilizando campos do banco de dados.

Para utilizar esse plugin, é necessário instalar o ImageMagick, que pode ser feito via MacPorts:
$ sudo port install ImageMagick

Eu fiz isso e obtive o seguinte erro:

On Mac OS X 10.5, tiff 3.8.2 requires Xcode 3.1 or later but you have Xcode 3.0.
Error: Target org.macports.extract returned: incompatible Xcode version
Error: The following dependencies failed to build: tiff xorg-libXext xorg-libX11 autoconf help2man p5-locale-gettext m4 automake libtool xorg-bigreqsproto xorg-inputproto xorg-kbproto xorg-libXau xorg-xproto xorg-libXdmcp xorg-util-macros xorg-xcmiscproto xorg-xextproto xorg-xf86bigfontproto xorg-xtrans xorg-libXt xorg-libsm xorg-libice
Error: Status 1 encountered during processing.

.
Para instalar o ImageMagick no Mac OS X 10.5 é preciso ter o Xcode 3.1 ou superior. Eu tinha o Xcode 3.0 instalado, então fui até a página de desenvolvedores da Apple e baixei a últma versão do Xcode para Mac OS X 10.5.

O endereço é https://connect.apple.com. Você precisa se logar para ter acesso aos downloads. Se você não tem cadastro, pode criar uma nova conta.

Depois de logado, clique no link Downloads, depois no menu da direita em Developer Tools, localize a seção Xcode 3.1.4 Developer Tools e faça o download dos 993 MB do arquivo Xcode 3.1.4 Developer DVD (Disk Image).

Após instalado o Xcode 3.1.4, você já pode instalar o ImageMagick via MacPorts e depois utilizar o PaperClip.

Para mais informações sobre o PaperClip, acesse esse link:
http://delicious.com/prodis.net/paperclip

Unix , , , , , ,

Métodos que retornam mais de um valor em Ruby

20, fevereiro, 2010

Nas últimas semanas, a equipe que eu trabalho estava desenvolvendo um web service onde havia a necessidade de renderizar o retorno de uma lógica de negócio em representações XML. Digo representações (no plural), pois para um retorno com sucesso a representação seria uma e para retorno com erro a representação seria outra.

Por exemplo, um retorno com sucesso:

<natural-person>
  <name>Prodis</name>
  <cpf>01234567890</cpf>
</natural-person>

E um retorno sem sucesso:

<error>
  <description>CPF inválido.</description>
</error>

Como era um web service em REST, para sucesso retornamos o código de status HTTP “200 OK” e, dependendo do não sucesso da operação, o código de status HTTP da resposta poderia ser “400 Bad Request”, “404 Not Found” ou qualquer outro código 4xx ou 5xx que melhor se adequasse.

Mantendo um controller magro, a idéia era somente instanciar uma classe de negócio, chamar um método e renderizar o retorno. Algo como:

class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new
    natural_person = business.search_by_cpf params[:cpf]

    # TODO: Renderizar pessoa física ou mensagem de erro
  end
end

A partir daqui a equipe iniciou uma discussão sobre a melhor forma de se obter o(s) retorno(s) esperado(s). O controller precisava saber se a consulta havia sido feita com sucesso, para renderizar um objeto NaturalPerson retornando o código de status HTTP 200, ou se a consulta não tivesse sucesso, renderizar a mensagem de erro retornando um código de status HTTP 4xx adequado.

Como “bons programadores .NET e Java”, a primeira coisa que pensamos foi lançar uma exceção customizada caso a consulta não tivesse sucesso, capturar essa exceção no controller e, através das informações de descrição de erro e código de status HTTP contidas nessa exceção, renderizar o retorno adequado.

class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new

    begin
      natural_person = business.search_by_cpf params[:cpf]

      render natural_person, 200
    rescue BusinessException => e
      render e.error, e.status_code
    end
  end
end

A gente não tinha visto muito código Ruby utilizando begin rescue, então essa solução não nos pareceu muito “Ruby way”. Achamos melhor pedir a opinião de alguém com mais experiência em Ruby. Perguntamos ao Rafael Rosa, que nos disse que cada vez que lançamos uma exceção em Ruby “alguma coisa ruim acontece no servidor” e consequentemente a aplicação ficará mais lenta.

Ele indicou um post falando a respeito:
http://www.simonecarletti.com/blog/2010/01/how-slow-are-ruby-exceptions

Rafael Rosa nos sugeriu retornar um array de duas posições: uma com o código de status HTTP e outra com o objeto a ser renderizado.

class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new
    result = business.search_by_cpf params[:cpf]

    render result[1], result[0]
  end
end

Resolveu, mas o código não ficou muito intuitivo. A partir daí imaginamos algumas outras soluções.

Retornar um hash:

class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new
    result = business.search_by_cpf params[:cpf]

    render result[:data], result[:status_code]
  end
end

Criar uma classe de retorno:

class SomeBusinessResult
  attr_accessor :status_code, :data
end
class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new
    result = business.search_by_cpf params[:cpf]

    render result.data, result.status_code
  end
end

Posteriormente, o Rafael Rosa também sugeriu essa última opção, mas criando uma Struct ao invés de uma classe.

Então eu sugeri o método search_by_cpf retornar dois valores. Todos da equipe me perguntaram: “Como assim retornar dois valores?”. Falei que em Ruby um método pode retornar vários valores, que vi isso no livro The Ruby Programming Language.

O código do controller ficou bem mais intuitivo:

class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new
    status_code, data = business.search_by_cpf params[:cpf]

    render data, status_code
  end
end

O método search_by_cpf está retornando tanto o código de status HTTP quanto os dados para serem renderizados:

class SomeBusiness
  def search_by_cpf(cpf)
    # Lógica de negócio aqui
    return 200, natural_person
  end
end

Note que mesmo a linha 4 sendo a última linha de instrução do método, o retorno de mais de um valor obrigatoriamente precisa utilizar o comando return.

Quando há mais de um valor de retorno para um método, os valores são colocados implicitamente dentro de uma array e essa array fica sendo o único retorno do método.

O mesmo resultado seria obtido dessa forma:

class Business
  def search_by_cpf(cpf)
    # Lógica de negócio aqui
    [200, natural_person]
  end
end

Quem está consumindo um método que retorna mais de um valor, pode utilizar o recurso de atribuição paralela do Ruby para distribuir os valores de retorno em variáveis distintas, como é o caso no nosso controller:

class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new
    status_code, data = business.search_by_cpf params[:cpf]

    render data, status_code
  end
end

O dinamismo do Ruby lhe oferece várias opções para você encontrar soluções para o mesmo problema ou questão. Cabe a você decidir qual melhor abordagem para seu tipo de problema. O interessante é você conhecer essas opções para facilitar a sua decisão.

Ruby , , ,

Não escreva código novo sem antes ter um teste falhando

16, fevereiro, 2010

O título desse post é uma frase de Kent Beck, autor do livro Test Driven Development: By Example. A idéia é que você sempre escreva testes antes de implementar qualquer código. Após o teste escrito falhar, você implementa o suficiente para fazer o teste passar. Com os testes passando, você está livre para refatorar (tanto implementação, quanto teste). A partir daí você cria um novo teste e segue o mesmo fluxo. Esse ciclo se repete até você ter toda a funcionalidade deseja implementada, ou seja, ter testes para todas as possibilidades da sua implementação.

Este é o “bê-a-bá” de TDD, mas na prática isso dificilmente acontece. Não porque não queremos fazer testes (se você não quiser escrever testes, o problema é todo seu), mas porque somos exímios programadores, desenvolvemos orientados a testes por anos, e não precisamos mais seguir os baby steps (passos de bebê), afinal somos programadores maduros.

Sendo assim, pulamos etapas: codificamos primeiro para depois escrever os testes, refatoramos mesmo com testes ainda não passando, escrevemos mais testes mesmo tendo testes anteriores falhando, e por aí vai.

Cuidado! Por mais que você seja um programador “fodão”, ainda sim você pode deixar de testar alguma coisa. Uma lógica de negócio, uma alternativa de fluxo ou uma condição de erro podem passar desapercebidas ao se pular as etapas básicas de TDD. Esse teste faltando, por mais simples que seja, pode causar um erro em ambiente de produção e causar transtornos para o cliente e/ou usuário final da sua aplicação.

Vamos utilizar como exemplo uma simulação de pareamento, onde uma dupla de desenvolvedores irá criar um método chamado positive_balance? para dizer se uma conta bancária, representada pela classe BankAccount, possui saldo positivo.

A linguagem utilizada será Ruby e o framework para testes será RSpec.

Os programadores são Félix (piloto do pareamento) e Péricles. Os dois concordam em iniciar criando a classe BankAccount com a declaração do método positive_balance? sem nenhuma implementação:

class BankAccount
  def positive_balance?

  end
end

- Legal, agora vamos escrever nosso teste. - diz Péricles.
- Para uma conta bancária possuir fundos é nessário que seu saldo seja maior que zero.

describe BankAccount do
  it "should have positive balance" do
    account = BankAccount.new
    account.value = 100.00
    account.positive_balance?.should be_true
  end
end

Eles rodam o teste:

F

1)
NoMethodError in 'BankAccount should have positive balance'
undefined method `value=' for #
./spec/bank_account_spec.rb:6:

Finished in 0.010015 seconds

1 example, 1 failure

E o resultado com erro diz a eles que não existe um atributo value na classe BankAccount. Félix e Péricles o criam:

class BankAccount
  attr_accessor :value

  def positive_balance?

  end
end

E executam o teste novamente:

F

1)
'BankAccount should have positive balance' FAILED
expected nil to be true
./spec/bank_account_spec.rb:7:

Finished in 0.010605 seconds

1 example, 1 failure

O teste falha. Então chegou a hora de escrever código novo, a implementação da funcionalidade que eles querem. Félix implementa o suficiente para o teste passar.

class BankAccount
  attr_accessor :value

  def positive_balance?
    true
  end
end

Péricles discorda totalmente.
- Cê tá louco, mano?! Vai retornar true para tudo?! O cara vai ter sempre saldo na conta?

Félix argumenta.
- A gente não precisa escrever código suficiente para o teste passar? Isso é suficiente.

E roda o teste:

.

Finished in 0.009987 seconds

1 example, 0 failures

- Viu? Passou. - finaliza Félix.
- Mas isso é muito baby step. - reclama Péricles - Vamos implementar o código real, ou seja:

class BankAccount
  attr_accessor :value

  def positive_balance?
    self.value > 0
  end
end

- Mas por que vamos implementar isso agora? Afinal nossos testes estão passando. - Félix rebate.
- Porque está na cara que esse código retornando true sempre não funciona.

Félix continua forçando a discussão.
- Como não funciona? Funciona sim, os testes estão passando.
- Funciona, mas a implementação está errada. - diz Péricles.
- Errada? Mas atende os requisitos até o momento. Afinal, os testes são para assegurar que a lógica do negócio está sendo cumprida.

Péricles fica pensativo.
- Mas o único teste que fizemos não está cobrindo todos os casos da lógica.
- Concordo com você, Péricles. E o que devemos fazer agora então?
- Devemos escrever um teste em que a conta bancária não irá ter fundos.
- Exatamente! - confirma Félix.

E eles continuam nesse linha de raciocínio até o final do pareamento.

Não estou aqui dizendo que você tem que sempre seguir à risca o Red Green Refactor do TDD, muito menos usar baby steps toda vez que você codificar (afinal, a vida não é um dojo), mas que você tenha atenção e controle do que está fazendo, tento o domínio da funcionalidade que está implementando.

Uma das maneiras de se conseguir isso é com pareamento. Seu par irá lhe ajudar a não deixar escapar nenhum teste. Outra maneira é com inspeção de código. De repente, outro desenvolvedor que não participou da implementação pode enxergar algo que você (e/ou seu par) não viu.

De qualquer forma, seja humilde. Use as etapas de TDD para funcionalidades ou lógica mais complexas. E também fique livre para burlar as regras para implementar coisas simples e funcionalidades básicas, ou quando estiver bastante à vontade e seguro do que está fazendo. Mas nunca, eu disse nunca, deixe de escrever os testes.

TDD , , ,

5ª Corrida Oral-B Prevenção do Câncer Bucal - 7 km

5, fevereiro, 2010

No domingo de 31 de janeiro de 2010 corri os 7 km da 5ª Corrida Oral-B Prevenção do Câncer Bucal.

A prova teve sua largada na avenida Santos Dumont e passou por largas e conhecidas avenidas como a Brás Leme e Olavo Fontoura, em um percurso praticamente plano.

Tempo total: 00:32:09

Tempo médio por km: 04:35

Tempo em cada km:

  1. 04:34
  2. 04:45
  3. 04:44
  4. 04:54
  5. 04:58
  6. 04:49
  7. 04:59
Foto de WebRun

Foto de WebRun

Foto de Treino Online

Foto de Treino Online

Foto de WebRun

Foto de WebRun

Foto de Treino Online

Foto de Treino Online

Foto de MidiaSport

Foto de MidiaSport

Foto de Ativo.com

Foto de Ativo.com

Esportes , , , , ,

A promoção Segunda Chance de certificações Microsoft voltou

5, fevereiro, 2010

A promoção Segunda Chance (”Second Shot”, em inglês) voltou para lhe ajudar a passar em seu próximo exame de certificação Microsoft. Essa promoção lhe dá o direito de refazer um exame caso você não consiga passar na primeira tentativa.

Você deve realizar tanto o primeiro e o segundo exame (se necessário) antes de 30 de junho de 2010. A promoção é valida para todos os exames de certificações Microsoft Learning IT professional, developer, project management, e Microsoft Dynamics.

Para se cadastrar na promoção Segunda Chance, siga as instruções dessa página:
http://www.microsoft.com/learning/Career/en/us/career-offer.aspx#certification

.NET , , , ,

Circuito do Sol - 5 km - A porta de entrada para o Pelotão Quênia

22, janeiro, 2010

Domingo, dia 17 de janeiro de 2010, corri a primeira prova do ano, o Circuito do Sol.

O percurso de 5 Km foi todo nas ruas da região do Pacaembu, largando em frente ao estádio, passando pela avenida Pacaembu, contornando antes de chegar no Elevado Costa e Silva e terminando com a chegada também em frente ao portão principal do estádio do Pacaembu.

Nessa prova consegui o índice para entrar no Pelotão Quênia. Fiz o percurso em 00:21:08, média abaixo de 04:30 por km, o tempo exigido para classificação no Pelotão Quênia.

Tempo total: 00:21:08

Tempo médio por km: 04:13

Tempo em cada km:

  1. 03:20
  2. 04:04
  3. 04:07
  4. 04:44
  5. 04:53
Foto de WebRun

Foto de WebRun

Foto de Treino Online

Foto de Treino Online

Foto de MidiaSport

Foto de MidiaSport

Foto de SportClick

Foto de SportClick

Esportes , , , , , ,

Melhores posts do ano de 2009

8, janeiro, 2010

85ª Corrida Internacional de São Silvestre - 15 km

7, janeiro, 2010

Para finalizar o ano, no dia 31 de dezembro de 2009 corri os 15 km da 85ª Corrida Internacional de São Silvestre.

Como sempre, a prova foi disputada nas ruas e avenidas do centro de São Paulo e da região da Paulista.

Foram mais de 21.000 inscritos, então imagina como estava difícil correr no meio daquela multidão. Mas valeu muito a participação nessa corrida tão tradicional de São Paulo.

Tempo total líquido: 01:25:31
Tempo total bruto: 01:37:07

Wellington Amaro, Henrique Soejima, Eu e Mauricio de Amorim

Wellington Amaro, Henrique Soejima, Eu e Mauricio de Amorim

Foto de SportClick

Foto de SportClick

Foto de WebRun

Foto de WebRun

Foto de Treino Online

Foto de Treino Online

Foto de MidiaSport

Foto de MidiaSport

Esportes , , , , ,