Posts com o tag ‘ruby’
11fev2011

A comunidade Ruby é conhecida por usar, criar e apoiar o open source, e isso fica evidente no evento online Ruby Masters Conf, organizado pela e-Genial e patrocinado pela Locaweb e outras empresas que apoiam a iniciativa.
O evento acontecerá no dia 25 e 26 de Fevereiro, e trará 12 palestrantes falando sobre as mais variados assuntos de desenvolvimento, abordando tecnologias do mundo Ruby, como ActiveRecord e gems, ferramentas, como Node.js e Git e também discussões sobre escalabilidade e testes automatizados.
Toda a arrecadação, tanto dos patrocínios quanto das doações dos participantes, será destinada a dois projetos open source: o Passenger e o RubyInstaller, ou seja, quem participar, além de aprender, ajuda dois projetos criados e mantidos pela comunidade Ruby!
As doações podem ser no valor de R$ 35, R$ 45 ou R$ 55, um valor pequeno e adequado a todos os bolsos. Visite o site oficial do evento para ver a programação do evento e conhecer um pouco sobre os palestrantes.
Não deixe de fazer sua inscrição e ajude a divulgar o evento!
Link deste post
Tags: doações, e-genial, evento online, GIT, Locaweb, Node.js, open source, ruby, Ruby Masters Conf
27jul2010
Como nas edições anteriores, esse ano no fisl11 tivemos várias palestras relacionadas com Ruby e Rails.

Quarta, 21 de julho
No primeiro dia, mostrei o uso do Spree, uma plataforma completa de comércio eletrônico desenvolvida em Ruby on Rails, como base de um novo sistema de loja virtual da Locaweb.
Nessa apresentação foram mostradas algumas técnicas de metaprogramação Ruby para lidar com as extensões do Spree e organizar melhor seu código.
Os slides da palestra “Locaweb + Spree: transformando código aberto em um projeto comercial” estão disponíveis nesse link:
http://www.slideshare.net/Prodis/locaweb-spree-transformando-cdigo-aberto-em-um-projeto-comercial
Quinta, 22 de julho
Neste dia, Daniel Lopes iniciou o “Mini-curso de Ruby e Rails”, introduzindo a linguagem de programação Ruby e os primeiros passos de Ruby on Rails. Essa primeira parte do curso teve duas horas de duração com a sala lotada.
ler mais
Link deste post
Tags: Comércio Eletrônico, fisl, HoraExtra, Prodis, rails, ruby, ruby on rails, Spree
14jul2010
Nos dias 21 a 24 de julho, será realizado em Porto Alegre o fisl11, o 11º Fórum Internacional de Software Livre.

O FISL é o maior evento de software livre da América Latina e a edição do ano passado atingiu a marca de 8.244 participantes.
Entre os assuntos que serão abordados, estão:
- Linux, Ubuntu, KDE, BSD
- Desenvolvimento em Ruby, Java, PHP, Python, Perl e Smalltalk
- Desenvolvimento de Jogos, Multimídia e Streaming
- Gerenciamento de Dados (SGBD, Storage, backup…)
- Hardware, Sistemas Embarcados e Robótica
- Segurança
- Software livre e negócios
- Educação e Inclusão Digital
A Locaweb mais uma vez estará presente, apoiando a iniciativa do Software Livre no Brasil.
Esse ano irei apresentar a palestra “Locaweb + Spree: transformando código aberto em um projeto comercial”, falando sobre os desafios, benefícios, dificuldades e lições aprendidas que a equipe de desenvolvimento de SaaS da Locaweb teve na utilização do Spree, uma plataforma de comércio eletrônico de código aberto, como base de seu novo sistema de loja virtual multi-usuário, desenvolvido em Ruby on Rails. O poder e o dinamismo do Ruby tiveram destaque, com grande utilização de metaprogramação nas extensões do Spree.

A apresentação será no dia 21/07 às 20h na sala 41-E fisl 5. Veja também a grade completa de programação e a planta com a localização das salas do evento.
Link deste post
Tags: Comércio Eletrônico, fisl, Prodis, ruby, ruby on rails, Spree
7abr2010
Muita gente acha (assim como eu achava há um tempo atrás) que não é possível utilizar debug (ou “depurar”, ou “debugar”) uma aplicação Ruby on Rails. Acredito que isso se deve ao fato de Ruby ser uma linguagem interpretada, diferente de outras linguagens que requerem compilação.

Existe uma forma muito simples de se “debugar” em Rails. Primeiro vamos instalar a gem ruby-debug.
$ sudo gem install ruby-debug
Agora vamos criar uma aplicação Rails de exemplo para utilizar o debug.
$ rails prodis-debug
$ cd prodis-debug
$ rails script/generate scaffold MyModel my_text:string my_value:float
$ rake db:migrate
E ao invés de subir a aplicação com script/server, iniciamos a aplicação em modo debug:
$ rdebug script/server
/Users/Prodis/Blog/code/prodis-debug/script/server:2
require File.expand_path('../../config/boot', __FILE__)
(rdb:1)
Esse é o momento de adicionarmos o(s) breakpoint(s) desejados, informando o arquivo e a linha do mesmo. Vamos colocar um breakpoint na ação create de MyModelsController:
(rdb:1) break app/controllers/my_models_controller.rb:43
Breakpoint 1 file /Users/Prodis/Blog/code/prodis-debug/app/controllers/my_models_controller.rb, line 43
E usamos o comando cont para prosseguir com o carregamento da aplicação no servidor.
(rdb:1) cont
=> Booting Mongrel
=> Rails 2.3.5 application starting on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
No browser, através do endereço http://localhost:3000/my_models/new, vamos criar um novo registro de MyModel.

Quando clicarmos no botão “Create”, a aplicação irá parar no breakpoint que colocamos na linha 43 de MyModelsController.
Breakpoint 1 at /Users/Prodis/Blog/code/prodis-debug/app/controllers/my_models_controller.rb:43
/Users/Prodis/Blog/code/prodis-debug/app/controllers/my_models_controller.rb:43
@my_model = MyModel.new(params[:my_model])
(rdb:6)
A partir daqui podemos investigar o código e ter acesso as suas variáveis. Por exemplo, exibindo os valores da variável params:
(rdb:6) p params
{"my_model"=>{"my_text"=>"I am some text", "my_value"=>"50.00"},
"commit"=>"Create", "authenticity_token"=>"qN9mUhJelZD0V/P2CB1fhBkQrIxFvvk+NRkY5m6EFWg=",
"action"=>"create", "controller"=>"my_models"}
Mas a maneira mais fácil de inspecionar variáveis é pelo irb, que irá se manter no contexto atual onde a aplicação está parada.
(rdb:6) irb
irb(#):001:0> params
=> {"my_model"=>{"my_text"=>"I am some text", "my_value"=>"50.00"},
"commit"=>"Create", "authenticity_token"=>"qN9mUhJelZD0V/P2CB1fhBkQrIxFvvk+NRkY5m6EFWg=",
"action"=>"create", "controller"=>"my_models"}
irb(#):002:0>
Saindo do irb (com o comando exit), nós voltamos para o ambiente de debug. Há uma série de comandos úteis para se utilizar. Um deles é o list, que exibe o trecho de código onde a aplicação está parada e indicando sua linha exata através de uma seta:
(rdb:6) list
[38, 47] in /Users/Prodis/Blog/code/prodis-debug/app/controllers/my_models_controller.rb
38 end
39
40 # POST /my_models
41 # POST /my_models.xml
42 def create
=> 43 @my_model = MyModel.new(params[:my_model])
44
45 respond_to do |format|
46 if @my_model.save
47 flash[:notice] = 'MyModel was successfully created.'
(rdb:6)
Para avançar até a próxima linha, usamos o comando next:
(rdb:6) next
/Users/Prodis/Blog/code/prodis-debug/app/controllers/my_models_controller.rb:45
respond_to do |format|
(rdb:6)
Para listar todos os comandos disponíveis, basta entrarmos no help:
(rdb:6) help
ruby-debug help v0.10.3
Type 'help ' for help on a specific command
Available commands:
backtrace delete enable help next quit show trace
break disable eval info p reload source undisplay
catch display exit irb pp restart step up
condition down finish list ps save thread var
continue edit frame method putl set tmate where
(rdb:6)
Também podemos visualizar a ajuda de um comando específico:
(rdb:6) help next
n[ext][+-]?[ nnn] step over once or nnn times,
'+' forces to move to another line.
'-' is the opposite of '+' and disables the force_stepping setting.
(rdb:6)
E para continuar a execução da aplicação, usamos o comando continue (ou um dos seus álias c ou cont)
(rdb:6) continue
Processing MyModelsController#create (for 127.0.0.1 at 2010-03-20 12:20:54) [POST]
Parameters: {"my_model"=>{"my_text"=>"I am some text", "my_value"=>"50.00"},
"commit"=>"Create", "authenticity_token"=>"qN9mUhJelZD0V/P2CB1fhBkQrIxFvvk+NRkY5m6EFWg="}
MyModel Create (0.5ms) INSERT INTO "my_models" ("my_text", "created_at", "updated_at", "my_value")
VALUES('I am some text', '2010-03-20 15:45:19', '2010-03-20 15:45:19', 50.0)
Redirected to http://localhost:3000/my_models/4
Completed in 1464995ms (DB: 1) | 302 Found [http://localhost/my_models]
Existe também uma outra maneira de se “debugar” com a gem ruby-debug. Nós podemos colocar uma instrução debugger (que na verdade é um método) em qualquer lugar do código para marcar como breakpoint. O Railscast #54 Debugging with ruby-debug mostra os passos de como fazer isso.
Eu particularmente gosto de configurar os breakpoints direto no Terminal, evitando assim ter que colocar qualquer informação de debug no código.
Esse esquema de debug não se restringe somente à aplicações Rails. Você pode muito bem utilizá-lo com código Ruby diretamente. Por exemplo, o código abaixo é do arquivo some_class.rb:
#lib/some_class.rb
class SomeClass
def some_method
# do something here
end
def other_method
# do another thing here
end
end
É só utilizar o comando rdebug passando o nome do arquivo:
$ rdebug lib/some_class.rb
/Users/Prodis/Blog/code/prodis-debug/lib/some_class.rb:2
class SomeClass
(rdb:1)
Para informações mais detalhadas sobre a gem ruby-debug, veja a documentação completa.
Post original:
http://prodis.pro.br/2010/03/20/utilizando-debug-em-aplicacoes-rails
.
Link deste post
Tags: Debug, Desenvolvimento de Software, linguagens de programação, Prodis, rails, ruby, ruby on rails, rubyonrails
22mar2010

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.
Post original:
http://prodis.pro.br/2010/02/20/metodos-que-retornam-mais-de-um-valor-em-ruby
.
Link deste post
Tags: Desenvolvimento de Software, Exception, linguagens de programação, Prodis, rails, REST, ruby, ruby on rails
18fev2010
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.
Post original:
http://prodis.pro.br/2010/02/16/nao-escreva-codigo-novo-sem-antes-ter-um-teste-falhando
.
Link deste post
Tags: Desenvolvimento de Software, Prodis, RSpec, ruby, tdd, Testes
18dez2009
Em um post anterior mostrei como serializar objetos em JSON utilizando .NET. Agora vamos fazer a mesma coisa com Ruby on Rails.
Vamos utilizar como exemplo uma classe de modelo chamada SomeFake:
class SomeFake < ActiveRecord::Base
end
Utilizando essa migration:
class CreateSomeFakes < ActiveRecord::Migration
def self.up
create_table :some_fakes do |t|
t.string :text
t.float :value
t.timestamps
end
end
def self.down
drop_table :some_fakes
end
end
No script/console vamos criar uma instância do modelo SomeFake com os seguintes dados:
>> fake = SomeFake.create :text => "I am a sample text.", :value => 150.85
=> #<SomeFake id: 1, text: "I am a sample text.", value: #<BigDecimal:18ac9f0,'0.15085E3',8(12)>,
created_at: "2009-12-13 19:43:28", updated_at: "2009-12-13 19:43:28">
Então queremos serializar a variável fake em JSON para obter o seguinte resultado:
{"id":1,"text":"I am a sample text.","value":150.85}
Para fazer isso, vamos chamar o método to_json na variável fake (estou usando o comando print para uma exibição melhor no console do JSON gerado):
>> print fake_json = fake.to_json
"{"some_fake": {"updated_at": "2009-12-13T19:43:28Z", "text": "I am a sample text.",
"id": 1, "value": 150.85, "created_at": "2009-12-13T19:43:28Z"}}"
O resultado que obtemos não é exatamente igual ao que estávamos querendo.
Primeiro, o nome do nosso modelo foi serializado como raiz do objeto em JSON. Isso aconteceu porque por padrão em uma aplicação Rails, a opção ActiveRecord::Base.include_root_in_json é configurada para true no arquivo config/initializers/new_rails_defaults.rb. Nós podemos alterar essa opção para false nesse arquivo, o que afeta a serialização em JSON de toda a aplicação, ou podemos alterá-lo no próprio script/console para nossos testes:
>> ActiveRecord::Base.include_root_in_json = false
=> false
Agora nosso objeto serializado fica assim:
>> print fake_json = fake.to_json
"{"updated_at": "2009-12-13T19:43:28Z", "text": "I am a sample text.", "id": 1,
"value": 150.85, "created_at": "2009-12-13T19:43:28Z"}"
A segunda diferença é que não queremos que os atributos de timestamps (created_at, updated_at) sejam serializados. Então vamos dizer para o método to_json não serializar esses atributos, utilizando a opção except:
>> print fake_json = fake.to_json(:except => [:created_at, :updated_at])
"{"text": "I am a sample text.", "id": 1, "value": 150.85}"
Para fazer o inverso, transformar dados em JSON para um objeto, criamos uma nova instância da classe SomeFake e chamamos o método from_json passando a variável fake_json como parâmetro:
>> other_fake = SomeFake.new
=> #<SomeFake id: nil, text: nil, value: nil, created_at: nil, updated_at: nil>
>> other_fake.from_json fake_json
=> #<SomeFake id: nil, text: "I am a sample text.", value: #<BigDecimal:1708a04,'0.15085E3',8(12)>,
created_at: nil, updated_at: nil>
Caso você precise serializar objetos em JSON sem os atributos timestamps com frequência, ao invés de sempre passar a opção except para o método to_json, podemos incluir um novo método na classe ActiveRecord::Base que faça a serialização sem esses atributos. Dessa forma, todos os nossos modelos terão essa funcionalidade.
Vamos chamar esse método de to_json_no_timestamps, o qual sua implementação é mostrada abaixo:
class ActiveRecord::Base
def to_json_no_timestamps(options = {})
timestamps_options = [:created_at, :updated_at]
if (options.has_key? :except)
if (options[:except].class == Array)
timestamps_options = options[:except] | timestamps_options
else
timestamps_options < < options[:except].to_sym unless options[:except].nil?
end
end
options[:except] = timestamps_options
to_json options
end
end
E então basta chamar nosso novo método em uma instância de qualquer modelo:
>> fake = SomeFake.first
=> #<SomeFake id: 1, text: "I am a sample text.", value: #<BigDecimal:1712798,'0.15085E3',8(12)>,
created_at: "2009-12-13 19:43:28", updated_at: "2009-12-13 19:43:28">
>> print fake_json = fake.to_json_no_timestamps
"{"text": "I am a sample text.", "id": 1, "value": 150.85}"
Post original:
http://prodis.pro.br/2009/12/13/serializacao-de-objetos-em-json-com-ruby-on-rails
.
Link deste post
Tags: ActiveRecord, JSON, linguagens de programação, Prodis, rails, ruby, ruby on rails, Serialização
15dez2009
Aqui na Locaweb utilizamos alguns projetos de Software Livre para auxiliar na criação de nossos produtos. Além de utilizarmos esses softwares, tentamos sempre que possível contribuir para a evolução dos projetos.

Pensando nisso, decidimos na última sexta-feira dedicar um dia inteiro de alguns desenvolvedores à contribuição ao Software Livre.
Desta vez, o projeto escolhido foi o Gitorious. O gitorious é um gerenciador de projetos git (uma versão livre do Github). Cerca de 12 desenvolvedores entraram em uma sala de reunião e seguiram uma série de tarefas pré-definidas em um backlog. Estas tarefas foram geradas a partir de problemas reais sentidos por clientes do gitorious (no caso os próprios desenvolvedores), além de sugestões de features que poderiam ser implementadas.

Alguns exemplos de histórias implementadas:
- Apresentar o conteúdo do arquivo README na página incial do projeto.
- Bug-Fix: Authorized_Keys deve ser gerado com permissão certa.
- Refatorações e melhoria na cobertura de testes
A empresa também patrocinou refrigerante e pizza para deixar o clima mais descontraído.

O resultado está publicado no próprio gitorious no branch do Fabio Hisamoto.
Link deste post
Tags: ferramentas, gitorious day, linguagens de programação, Linux, rails, ruby, ruby on rails
23out2009
Spree é uma ferramenta de e-commerce, open source, desenvolvida em Ruby on Rails. A licença do Spree é New Free BSD, desta forma podemos utiliza-lo para fins comerciais.
Recursos do Spree:
- Projeto extensível;
- Suporta a última versão do Ruby on Rails;
- Upgrades Simples;
- Utiliza o framework jQuery;
- Suporte para múltiplos idiomas;
- Suporta UPS, FedEx e USPS;
- Utiliza o ActiveMerchant que permite o acesso a mais de 50 diferentes gateways de pagamento e serviços, incluindo Paypal e Authorize.net;
- Suporte a taxonomia, permitindo ao lojista categorizar seus produtos de forma complexa;
- Página de checkout única, minimizando uma possível confusão do cliente;
- Suporte para envio de e-mail de confirmação de compra;
- Segue as melhores práticas de design RESTful;
- Permite ao lojista ter o controle do seu estoque de produtos;
- Framework Blueprint and Sass para personalização de CSS;
- Suporta a customização para taxa de vendas;
- Motor de busca amigável;
- Google Analytics;
Através da administração o usuário poderá configurar opções de entrega, formas de pagamento, variantes para produtos, cadastro de produtos, entre outras funcionalidades. Veja a demo do Spree.
Pagamento Certo no Spree
Utilizamos a gem do Pagamento Certo desenvolvida pelo Akita. Com isso criamos uma extension do Pagamento Certo que faz a “ponte” para ligar as duas coisas. A extension modifica o processo de checkout para utilizar automaticamente o serviço Pagamento Certo.
Correios no Spree
A extension dos Correios foi desenvolvida, inicialmente, para efetuar o cálculo de frete por Sedex. A proposta é disponibilizar todas as outras formas de entrega que os Correios oferecem (Sedex, Sedex 10, e-Sedex e PAC), apenas com a criação de novos Calculators. Atualmente o cálculo de frete é efetuado consumindo um Webservice SOAP.
Para mais informações de como instalar o Spree com as extensions do Pagamento Certo e dos Correios, siga as instruções da Wiki.
Link deste post
Tags: Correios, e-commerce, linguagens de programação, Pagamento Certo, rails, ruby, ruby on rails, Spree
14out2009
No desenvolvimento com Ruby on Rails por inúmeras vezes precisamos abrir o terminal para checar os registros de um determinado objeto. Dependendo da quantidade de registros do objeto sua visualização no terminal fica um pouco confusa.
Neste exemplo utilizo um projeto de testes que é o cadastro de restaurantes. Veja a saída no terminal quando acesso o script/console e executo o comando Restaurante.all:

Para alegrar os olhos do desenvolvedor, temos a gem hirb que faz a formatação dos dados no terminal. Vamos instalar a gem. Para isso execute o comando:
sudo gem install hirb
Com a gem instalada temos duas possibilidades de utilização.
Adicionando a gem no terminal
Abra o terminal no diretório do projeto desejado e execute o comando:
script/console
Em seguida:
require ‘hirb’
E por fim:
Hirb::View.enable
Pronto! Agora basta executar o comando Restaurante.all novamente, que você terá uma saída parecida com esta:

Se utilizado da forma mencionada acima, toda vez que carregar o terminal com script/console para o projeto, será necessário executar os comandos novamente.
Adicionando a gem direto no projeto
Esta opção é a mais fácil.
Abra o arquivo /config/environments/development.rb
No final do arquivo adicione as linhas:
require ‘hirb’
Hirb::View.enable
Pronto! Basta abrir o terminal, dentro do diretório do projeto e executar o comando Restaurante.all através do script/console, você terá o mesmo retorno mencionado acima, veja:

Ajudou em meu desenvolvimento e espero que ajude o seu!
Post original:
http://www.ortiz.blog.br/dicas/gem_hirb_alegria_aos_olhos_do_desenvolvedor/
Link deste post
Tags: gem hirb, hirb, linguagens de programação, rails, ruby, ruby on rails
21set2009
Vamos usar um pouco de magia negra do Ruby para encontrar uma alternativa à implementação clássica do Design Pattern Decorator apresentado pela GoF.

.
Este post é a continuação de dois anteriores:
Se você ainda não os leu, recomendo que o faça para entender o contexto do exemplo onde estamos aplicando o Design Pattern Decorator. O ponto onde paramos no último post foi o meu descontentamento em decorar um objeto Cheeseburger de uma forma não muito intuitiva.
ler mais
Link deste post
Tags: arquitetura, Decorator, Desenvolvimento de Software, Design Pattern, linguagens de programação, Magia Negra, Metra Programação, Module, Prodis, ruby
14set2009
No post Cheeseburgers, Decorators e Mocks eu mostrei um exemplo prático de utilização do Design Pattern Decorator, que começa com um design usando herança, desaclopa usando composição e finalmente aplica Decorator. Tudo isso foi feito em .NET com C#. Agora vamos fazer o mesmo exemplo de Decorator Pattern utilizando Ruby. Para entender melhor o contexto do exemplo utilizado, sugiro que você leia antes o post anterior.

Além de Ruby, utilizarei o RSpec como ferramenta de testes unitários. No final do post há os links para baixar o código completo.
.
Cheeseburgers com Ruby
A principal diferença de implementação do Design Pattern Decorator em Ruby é a não utilização de classes abstratas (ou interfaces). No Ruby não existe classes abstratas ou interfaces, com isso eliminamos um elemento na interação entre os objetos.
Veja como fica o diagrama de classes com a estrutura do padrão:

Decorator em Ruby
Onde:
- Component define a interface para objetos que podem ter responsabilidades acrescentadas aos mesmos dinamicante;
- ConcreteComponent define um objeto para o qual responsabilidades adicionais podem ser atribuídas;
- Decorator mantém uma referência para um objeto Component e acrescenta responsabilidades ao componente.
Vamos criar as classes do nosso exemplo, mas diferente do post anterior, vou mostrar primeiros os testes e depois as implementações.
Primeiro vamos fazer o teste da classe Cheeseburger, que servirá como ConcreteComponent.
describe Cheeseburger, " when created" do
it "should have default calories and description" do
cheeseburger = Cheeseburger.new
cheeseburger.calories.should == 300
cheeseburger.description.should == "Bread, Hamburger, Cheese"
end
end
O teste fala por si só. Agora vamos à implementação.
class Cheeseburger
attr_reader :description, :calories
def initialize
@description = "Bread, Hamburger, Cheese"
@calories = 300
end
end
O próximo passo são os testes da classe Sandwich, o nosso Component.
describe Sandwich do
before(:all) do
@real_sandwich = mock Cheeseburger
@sandwich = Sandwich.new @real_sandwich
end
it "should call description in real sandwich" do
@real_sandwich.should_receive(:description).once.and_return("Sandwich description")
@sandwich.description.should == "Sandwich description"
end
it "should call calories in real sandwich" do
@real_sandwich.should_receive(:calories).once.and_return(150)
@sandwich.calories.should == 150
end
end
O bloco before (linhas 2 a 5) é executado somente uma vez antes da execução dos testes. Nele criamos um mock da classe Cheeseburger que é passado como parâmetro na criação da instância da classe Sandwich. Os testes asseguram que os métodos description (linha 8 ) e calories (linha 13) são chamados nos mocks e seus valores são retornados corretamente (linhas 9 e 14).
Então vamos à implementação da classe Sandwich.
class Sandwich
def initialize(real_sandwich)
@real_sandwich = real_sandwich
end
def description
@real_sandwich.description
end
def calories
@real_sandwich.calories
end
end
Agora só faltam nossos Decorators. Seguem os testes da classe Corn:
describe Corn do
before(:all) do
@sandwich = mock Sandwich
@corn = Corn.new @sandwich
end
it "should add corn description to sandwich description" do
@sandwich.should_receive(:description).once.and_return("Sandwich description")
@corn.description.should == "Sandwich description, Corn"
end
it "should add corn calories to sandwich calories" do
@sandwich.should_receive(:calories).once.and_return(100)
@corn.calories.should == 170
end
end
Da mesma forma que fizemos nos testes da classe Sandwich, criamos uma instância de Corn passando um mock da classe Sandwich no bloco before (linhas 2 a 5). Os testes asseguram que a descrição e as calorias do milho será adicionadas ao sanduíche.
Vamos implementar nossa classe Corn.
class Corn < Sandwich
def initialize(real_sandwich)
super real_sandwich
end
def description
"#{@real_sandwich.description}, Corn"
end
def calories
@real_sandwich.calories + 70
end
end
A classe Corn herda a classe Sandwich e o método super chamado na linha 3 vai chamar o método initialize da classe base. O método description (linhas 6 a 8 ) é sobrescrito para retornar a descrição do sanduíche original juntamente com a descrição de milho. O método calories também é sobrescrito (linhas 10 a 12) e soma o retorno do método calories do sanduíche original com as calorias do milho.
O mesmo é feito para as classes OnionRings e PepperSauce, tanto na implementação como nos testes.
class OnionRings < Sandwich
def initialize(real_sandwich)
super real_sandwich
end
def description
"#{@real_sandwich.description}, Onion Rings"
end
def calories
@real_sandwich.calories + 140
end
end
class PepperSauce < Sandwich
def initialize(real_sandwich)
super real_sandwich
end
def description
"#{@real_sandwich.description}, Pepper Sauce"
end
def calories
@real_sandwich.calories + 20
end
end
.
E o nosso diagrama de classes fica assim:

Cheeseburgers com Ruby
Note que a classe Cheeseburger não herda de Sandwich. Como no Ruby as coisas são flexíveis, na hora de instanciar um Decorator (Corn, OnionRings ou PepperSauce) você não precisa necessariamente passar como parâmetro um objeto que herde da classe Sandwich. Basta que o objeto passado possua os métodos description e calories. Aqui é que entra em ação o duck typing do Ruby:
If it walks like a duck and quacks like a duck, I would call it a duck.
No nosso caso, se um o objeto se parece com um sanduíche, ou seja, tem uma descrição e quantidade de calorias, então nós o chamamos de saduíche. O duck typing permite a utilização de polimorfismo sem herança.
Agora já podemos montar os cheeseburgers de Itararé e Ilhéus, bem como outras variações. Veja alguns testes:
describe Cheeseburger do
before(:each) do
@cheeseburger = Cheeseburger.new
end
it "should be an Itarare Cheeseburger" do
@cheeseburger = Corn.new @cheeseburger
@cheeseburger.description.should == "Bread, Hamburger, Cheese, Corn"
@cheeseburger.calories.should == 370
end
it "should be an Itarare Cheeseburger with onion rings" do
@cheeseburger = Corn.new @cheeseburger
@cheeseburger = OnionRings.new @cheeseburger
@cheeseburger.description.should == "Bread, Hamburger, Cheese, Corn, Onion Rings"
@cheeseburger.calories.should == 510
end
# Outros testes
end
A decoração de objetos segue quase a mesma linha do fizemos em C# no post anterior. Vamos detalhar o segundo teste para entendermos as diferenças. A variável @cheeseburger é decorada duas vezes, uma vez com a classe Corn (linha 13) e outra com a classe OnionRings (linha 15).
Quando o método calories da variável @cheeseburger é chamado na linha 16, estamos chamando o método calories do último decorador, ou seja, da classe OnionRings. Depois contamos com a delegação para adicionar as calorias dos ingredientes. Veja o acontece:
- OnionRings chama o método calories do objeto Corn que foi passado como parâmetro quando a classe OnionRings foi instanciada;
- Corn chama o método calories do objeto Cheeseburger que foi passado como parâmetro quando a classe Corn foi instanciada;
- Cheeseburger retorna 300 calorias;
- Corn adiciona suas 70 calorias ao retorno do objeto Cheeseburger e retorna 370 calorias;
- OnionRings adiciona suas 140 calorias ao retorno do objeto Corn e retorna 510 calorias.
Em relação à implementação em C#, o Ruby eliminou 4 passos.
.
Legal, mas nós podemos melhorar?
Sim, nós podemos.
A classe Sandwich não faz nada mais além de delegar as chamadas dos métodos description e calories. Como nós temos somente dois métodos, isso até que não é um problema. Mas e se tivéssemos, por exemplo, uns 10 métodos? Vai ser um saco ficar escrevendo esses métodos que delegam para outros métodos.
Para resolver isso, o Ruby nos fornece um módulo chamado Forwardable, que gera automaticamente todos esses métodos de delegação para nós. Vamos reescrever a classe Sandwich utilizando o módulo Fowardable.
require 'forwardable'
class Sandwich
extend Forwardable
def initialize(real_sandwich)
@real_sandwich = real_sandwich
end
def_delegators :@real_sandwich, :description, :calories
end
O módulo Fowardable fornece o método def_delegators, que recebe dois ou mais parâmetros. O primeiro parâmetro é o nome da variável de instância que será usada para delegar as chamadas dos métodos. Os outros parâmetros são os nomes dos métodos que queremos delegar. Na linha 10, o método def_delegators adiciona os métodos description e calories para a classe Sandwich, e cada um deles irá delegar sua chamada para o respectivo método para o objeto armazenado na variável @real_sandwich.
Um detalhe importante é que estamos extendendo o módulo Fowardable (linha 4) ao invés de incluí-lo (utilizando o método include). Isso se deve ao fato que queremos adicionar métodos de classe e não métodos de instância.
Depois dessa alteração na classe Sandwich, rodamos os testes e tudo funciona. Mas algo que ainda me incomoda um pouco é ter que escrever uma linha de código para cada novo ingrediente que acrescentamos no cheeseburger. Vamos pegar como exemplo esse teste:
describe Cheeseburger do
it "should be an Cheeseburger with pepper sauce, onion rings and corn" do
cheeseburger = Cheeseburger.new
cheeseburger = PepperSauce.new
cheeseburger = OnionRings.new
cheeseburger = Corn.new
cheeseburger.description.should == "Bread, Hamburger, Cheese, Pepper Sauce, Onion Rings, Corn"
cheeseburger.calories.should == 530
end
end
Na linha 2 criamos um cheeseburger e nas linhas seguintes adicionamos seus ingredientes. Se quisermos fazer a mesma coisa em somente uma linha, vai ficar assim:
describe Cheeseburger do
it "should be an Cheeseburger with pepper sauce, onion rings and corn" do
cheeseburger = Corn.new(OnionRings.new(PepperSauce.new(Cheeseburger.new)))
cheeseburger.description.should == "Bread, Hamburger, Cheese, Pepper Sauce, Onion Rings, Corn"
cheeseburger.calories.should == 530
end
end
Hum… A descrição do teste diz que deveria ser um cheeseburger com molho de pimenta, cebola e milho. Mas lendo o código, dá a impressão que estamos criando um milho com cebola, molho de pimenta e cheeseburger. E se a gente tentar deixar mais legível invertendo a ordem da criação dos objetos? Vamos alterar a linha 3 assim:
describe Cheeseburger do
it "should be an Cheeseburger with pepper sauce, onion rings and corn" do
cheeseburger = Cheeseburger.new(PepperSauce.new(OnionRings.new(Corn.new)))
cheeseburger.description.should == "Bread, Hamburger, Cheese, Pepper Sauce, Onion Rings, Corn"
cheeseburger.calories.should == 530
end
end
E adivinha o que acontece! O teste não passa! O método initialize da classe Cheeseburger não tem nenhum parâmetro, então é lançado um ArgumentError ao rodar o teste. Além disso, o método initialize da classe Corn precisa receber um parâmetro, o qual não foi passado. Mais um ArgumentError.
Você está conformado com isso? Eu não. Sendo assim, vamos ter que usar um pouco de magia negra para melhorar essa situação. Mas isso fica para o próximo post.
Dúvidas, questionamentos, discórdias, sugestões? Deixe seu comentário.
O código completo com todas as classes e seus testes está disponível aqui no meu Github.
.
Referências:
Post original:
http://prodis.pro.br/2009/09/13/cheeseburgers-decorators-e-ruby
.
Link deste post
Tags: arquitetura, Decorator, Desenvolvimento de Software, Design Patterns, Duck Typing, Fowardable, linguagens de programação, OOP, Prodis, Programação Orientada a Objetos, RSpec, ruby
1set2009
A equipe da Phusion acaba de lançar uma nova versão do Passenger [1] , o modulo de deploy para aplicações Ruby on Rails, essa nova versão esta totalmente focada em bugfixes em relação as anteriores.
Pelo ambiente Locaweb ser diferenciado, foi desenvolvido um patch para ser incluido a essa versão do mod_rails mantendo a compatibilidade para as aplicações que se encontram em produção atualmente.
Para os nossos clientes que utilizam Rails estaremos atualizando os nossos servidores com essa nova versão em alguns dias.
[1] http://blog.phusion.nl/2009/09/01/phusion-passenger-2-2-5-released/
Link deste post
Tags: Hospedagem, linguagens de programação, Linux, passenger, rails, ruby
23jul2009
Num artigo escrito em março, o Fabio Akita mostrou como fazer acesso a um banco de dados MS SQL Server por uma aplicação Rails. Apresentaremos nesse artigo uma alternativa mais simples, usando JRuby. Dessa forma, não é necessário configurar ODBC da máquina e a solução é exatamente a mesma, independente da plataforma ser Linux, Windows ou Mac.
Quem nunca trabalhou com Java ou JRuby terá um pouco mais de trabalho para configurar seu ambiente. Estamos supondo que a instalação da JDK já foi feita. Para instalar o JRuby, basta acessar o site do projeto, baixar e descompactar o arquivo em alguma pasta da sua máquina. Na ocasião desse artigo, baixamos a versão 1.3.1 do JRuby. Uma vez que o executável do JRuby esteja instalado na máquina juntamente com a JDK, o jruby deverá também estar incluído no seu PATH. Além disso, você precisa instalar o Driver JDBC para SQLServer no seu JRuby. Para isso, basta baixar o driver. O driver vem dentro de um arquivo .ZIP – o usado por nós foi o da versão 1.2.2. Coloque o arquivo jtds-1.2.2.jar no diretório /lib da sua instalação do JRuby.
A partir daí basta seguir os seguintes passos para criar uma aplicação Rails
- Execute os comandos:
jruby -S gem install rails
jruby -S gem install activerecord-jdbc-adapter
- jruby -S rails testesqlserver
- Edite o arquivo config/database.yml e coloque o seguinte conteúdo no banco de development:
adapter: jdbc
url: jdbc:jtds:sqlserver://seu.servidor.sql.server.com/suabase
driver: net.sourceforge.jtds.jdbc.Driver
username: usuario
password: senha
- Entre no diretório testesqlserver e execute os comandos:
jruby -S script/generate model MyTable name:string
jruby -S rake db:migrate
Se tudo correu bem até aqui, nesse ponto a migration vai criar a tabela my_tables no seu banco de dados. Isso significa que sua conexão já está funcionando. Não fizemos muitos testes de performance para saber se esse tipo de configuração pode ser usado em aplicações críticas, mas algumas funções básicas funcionaram muito bem:
>> (1..10).each do |t|
?> a = MyTable.new
>> a.name = "#{t}"
>> a.save
>> end
=> 1..10
>> MyTable.find(:all)
=> [#<MyTable id: 2, name: nil, created_at: "2009-07-23 18:46:35", updated_at: "2009-07-23 18:46:35">, #<MyTable id: 3, name: "1", created_at: "2009-07-23 18:49:22", updated_at: "2009-07-23 18:49:22">, #<MyTable id: 4, name: "2", created_at: "2009-07-23 18:49:22", updated_at: "2009-07-23 18:49:22">, #<MyTable id: 5, name: "3", created_at: "2009-07-23 18:49:22", updated_at: "2009-07-23 18:49:22">, #<MyTable id: 6, name: "4", created_at: "2009-07-23 18:49:22", updated_at: "2009-07-23 18:49:22">, #<MyTable id: 7, name: "5", created_at: "2009-07-23 18:49:22", updated_at: "2009-07-23 18:49:22">, #<MyTable id: 8, name: "6", created_at: "2009-07-23 18:49:22", updated_at: "2009-07-23 18:49:22">, #<MyTable id: 9, name: "7", created_at: "2009-07-23 18:49:22", updated_at: "2009-07-23 18:49:22">, #<MyTable id: 10, name: "8", created_at: "2009-07-23 18:49:22", updated_at: "2009-07-23 18:49:22">, #<MyTable id: 11, name: "9", created_at: "2009-07-23 18:49:22", updated_at: "2009-07-23 18:49:22">, #<MyTable id: 12, name: "10", created_at: "2009-07-23 18:49:22", updated_at: "2009-07-23 18:49:22">]
>> MyTable.find(:all).each do |m|
?> m.delete
>> end
=> [#<MyTable id: 2, name: nil, created_at: "2009-07-23 18:46:35", updated_at: "2009-07-23 18:46:35">, #<MyTable id: 3, name: "1", created_at: "2009-07-23 18:49:22", updated_at: "2009-07-23 18:49:22">, #<MyTable id: 4, name: "2", created_at: "2009-07-23 18:49:22", updated_at: "2009-07-23 18:49:22">, #<MyTable id: 5, name: "3", created_at: "2009-07-23 18:49:22", updated_at: "2009-07-23 18:49:22">, #<MyTable id: 6, name: "4", created_at: "2009-07-23 18:49:22", updated_at: "2009-07-23 18:49:22">, #<MyTable id: 7, name: "5", created_at: "2009-07-23 18:49:22", updated_at: "2009-07-23 18:49:22">, #<MyTable id: 8, name: "6", created_at: "2009-07-23 18:49:22", updated_at: "2009-07-23 18:49:22">, #<MyTable id: 9, name: "7", created_at: "2009-07-23 18:49:22", updated_at: "2009-07-23 18:49:22">, #<MyTable id: 10, name: "8", created_at: "2009-07-23 18:49:22", updated_at: "2009-07-23 18:49:22">, #<MyTable id: 11, name: "9", created_at: "2009-07-23 18:49:22", updated_at: "2009-07-23 18:49:22">, #<MyTable id: 12, name: "10", created_at: "2009-07-23 18:49:22", updated_at: "2009-07-23 18:49:22">]
>> MyTable.find(:all)
=> []
Link deste post
Tags: linguagens de programação, rails, ruby, ruby on rails, sql
30jun2009
A primeira apresentação que assisti no fisl10 foi muito boa. Lucas Húngaro mostrou formas de criar aplicações Ruby on Rails com testes, passando um pouco da sua experiência em desenvolvimento Web.

A palestra foi dividida em quatro partes: Fundamentos, Abordagens, Como eu desenvolvo com testes no Rails e Dicas. Mas separei aqui em mais duas: Boas Práticas e Maus Sinais.
Fundamentos
- TDD, BDD e suas suas diferenças.
Abordagens
- Outside-in: perspectiva dos usuários, testes de fora para dentro. São os testes de aceitação, mostram onde chegar.
- Inside-out: perspectiva do desenvolvedor, testes de dentro para fora. São os testes unitários, mostram como chegar.
- Atenção para não duplicar testes. Por exemplo, quando testar validações no model, não testar novamente essa validação no teste do controller que usa esse model.
Como eu desenvolvo com testes no Rails
- Testes unitários somente para models, testes funcionais mínimos e testes de aceitação guiando seu design de alto nível.
- Não fazer testes para helpers: se há uma complexidade em um helper que precise de um teste, essa lógica não deveria estar na View. Os helpers devem contém formatação e não lógica de negócio.
- Processo ideal: criar uma feature no Cucumber e vai descendo do alto nível (browser, session/routes, view) para os níveis mais baixos (controllers, models).
Dicas
- Use factories para fazer testes.
- Especifique os casos de falha, não crie testes somente para a execução perfeita da funcionalidade.
- Os testes precisam revelar o comportamento e a intenção. Testes com nomes de métodos ficam esquisitos, coloque no nome do teste o comportamento que você está testando.
- Testes não precisam ser totalmente DRY (Don’t Repeat Yourself), pois devem ser muito fáceis de entender, somente “batendo o olho”.
- Use com moderação objetos de substituição (mocks, stubs, proxies, etc).
- Não utilize mocks como stubs.
- Não confie cegamente em métricas: é muito fácil criar testes que não testam nada e que aumenta a cobertura de testes.
Boas práticas
- Não substitua (com mocks, por exemplo) o objeto que você está testando.
- Crie wrappers para objetos que você não é dono, ao invés de tentar modificar os objetos de terceiros nos seus testes, já que no Ruby você tem o poder para alterar tudo.
- Teste também o que não deve acontecer, sendo explícito a respeito disso.
Maus sinais
- Métodos de setup longos: você está testando muita coisa ao mesmo tempo e seu design provalmente está acoplado.
- Falta de testes de integração: você irá perder a sincronia com as alterações que são feitas nos testes unitários.
.
Grande parte do que foi passado na apresentação se aplica em qualquer plataforma onde você esteja desenvolvendo orientado a testes.
O arquivo PDF com slides da apresentação você pode baixar aqui. O que achei muito legal desses slides é que eles vêm comentados com o texto de base para a apresentação, ou seja, praticamente você pode “ler” a apresentação que foi feita.
.
Link deste post
Tags: bdd, cucumber, dry, fisl, fisl10, linguagens de programação, mock, Prodis, rails, ruby, ruby on rails, stub, tdd, Testes