/* CODIFICANDO */

Relatos de um programador em contínua aprendizagem.

Archive for the ‘ruby’ Category

Como criar gems que instalam executáveis na sua máquina

sem comentários

Precisei criar uma gem que gerasse um executável no ambiente, sendo possível assim executar a biblioteca como um utilitário de linha de comando. Encontrei muito pouca documentação sobre como fazer isso, então vou deixar aqui caso seja útil para mais alguém.

O segredo é manter uma pasta bin dentro da estrutura de diretórios da sua gem e colocar ali dentro os arquivos que você deseja que se tornem executáveis após a instalação da gem. Você deve salvar estes arquivos sem a extensão .rb, caso contrário terá que escrever .rb na linha de comando também para inicializar o programa. Em sistemas POSIX você pode criar arquivos executáveis e usar o “shebang” para instruir o shell sobre qual programa deve ser utilizado para executar o conteúdo do arquivo. Assim, basta adicionar a seguinte linha ao início do arquivo:

#! /usr/bin/env ruby

Após criar seu arquivo dentro de bin, basta adicionar o mesmo como um executável à sua gemspec. Estou usando o Jeweler para criar minhas gems, então tudo o que preciso fazer é gerar minha gemspec usando a seguinte raketask que o arquivo será criado corretamente:

rake gemspec

Isso vai criar um arquivo gemspec contendo a seguinte linha:

Gem::Specification.new do |s|
  ...
  s.executables = ["bla", "ble", "bli"]
  ...
end

Onde bla, ble, bli são arquivos dentro de bin que devem se transformar em executáveis após a instalação da gem. Quando sua gem for instalada, estes arquivos será copiados para dentro de /usr/bin e receberão permissão para serem executados.

Escrito por cassiomarques

Novembro 11, 2009 em 12:30 am

Publicado em Desenvolvimento, ruby

Etiquetado com , ,

Economize tempo e mantenha o foco ao rodar seus testes

com 6 comentários

Testar é bom. Esperar os testes rodarem, não. Quando um teste específico falha, o ideal é rodar apenas esse teste e não toda a suíte de testes novamente. Depois que esse cara estiver passando, ai sim rodamos tudo, para termos certeza de que nada mais quebrou. Além disso, se você estiver escrevendo testes para uma app Rails, sabe que sempre que você faz rake spec todo o seu banco é recarregado. Ok, legal… mas e se você quiser rodar apenas um arquivo de specs onde tudo é feito com mocks/stubs e não há acesso ao banco? Porque esperar todo o banco ser carregado?

Seguem abaixo algumas dicas para agilizar a execução dos seus testes.

Para o Rspec

Rodar apenas um arquivo (carregando o banco de teste):

rake spec SPEC=[caminho para o arquivo]_spec.rb

Rodar apenas um arquivo (sem carregar o banco, útil por exemplo para rodar specs de controllers/helpers quando utiliza-se mocks/stubs)

spec [caminho para o arquivo]_spec.rb

Rodar apenas um teste específico dentro do seu arquivo (10 é o número da linha onde se encontra o bloco ‘it’ que você quer executar. Funciona de forma idêntica caso você use o comando ’spec’ ao invés de ‘rake’)

rake spec SPEC=[caminho para o arquivo]_spec.rb:10

Para o Cucumber

Rodar apenas um arquivo

rake features FEATURE=features/[seu arquivo].feeature

Rodar apenas um cenário (10 é a linha onde começa a declaração do cenário)

rake features FEATURE=features/[seu arquivo].feature:10

Escrito por cassiomarques

Outubro 7, 2009 em 9:06 pm

Publicado em rails, rspec, ruby, testes

Etiquetado com , ,

Dica rápida: Testando igualdade entre arrays de models

com um comentário

É comum testarmos métodos que retornam arrays de models. Um bom exemplo disso é quando testamos named_scopes (eu gosto de testar named_scopes pra valer, batendo no banco e não somente usando macros como as do Remarkable). A grande pagadinha é que a ordem na qual os objetos serão retornados do banco é totalmente imprevisível, a não ser que seu método já traga os itens ordenados por padrão. Assim, testes como o exemplo abaixo podem hora passar, hora não:

describe ".active" do
  before :each do
    @user1 = User.make :active => true
    @user2 = User.make :active => false
    @user3 = User.make :active => true
    @user4 = User.make :active => false
  end

  it "retorna somente usuários ativos" do
    User.active.should == [@user1, @user4]
  end
end

Uma maneira segura de testar isso é usar a classe Set, da biblioteca padrão do Ruby. Um Set representa um conjunto matemático, sendo que a igualdade entre dois conjuntos existe quando ambos os conjuntos possuem exatamente os mesmos elementos, independentemente da ordenação. Assim, podemos reescrever a linha da spec que faz a asserção e termos certeza de que ele terá sempre o mesmo comportamento:

User.active.to_set.should == Set.new([@user1, @user4])

Escrito por cassiomarques

Setembro 23, 2009 em 1:45 am

Publicado em rails, ruby, testes

Etiquetado com , , ,

Testar atributos do tipo datetime com o Rspec

com 2 comentários

Você já tentou usar o Rspec para testar se o timestamp gravado em um campo datetime é igual a uma determinada data/horário? O código abaixo possivelmente irá falhar miseravelmente:

@blabla.update_attributes(:foo => "bar)
@blabla.updated_at.should == Time.now

o Time.now é bem preciso e vai pegar várias casas na faixa dos milissegundos, e isso pode gerar alguma diferença entre o retorno de Time.now quando a coluna foi atualizada no banco e quando você realiza a asserção no teste. Uma solução para o problema é fazer:

@blabla.updated_at.should be_close(Time.now, 1.second)

Esse teste passará para qualquer timestamp com uma diferença de +- 1 segundo em relação ao retorno do Time.now no momento da asserção. Altere esse delta T como desejar :).

Essa solução também é indicada para testar valores numéricos de ponto flutuante.

Escrito por cassiomarques

Julho 3, 2009 em 11:04 pm

Publicado em rails, rspec, ruby, testes

Etiquetado com , ,

Booleanize 0.2 com configuração global

sem comentários

Dei uma atualizada no booleanize e agora ele aceita configuração global. O que isso quer dizer? Significa que se você usa quase sempre as mesmas strings para true ou false, não vai mais precisar ficar duplicando o código em todos os models. Basta adicionar algo como isso no seu environment.rb:

Booleanize::Config.default_strings :true => "Yes", :false => "No"

Assim, toda vez que você usar booleanize :some_boolean_attr nos seus models, não será precisa especificar quais strings deverão ser usadas. Mas se em um caso específico você precisar usar outras strings, basta declarar da forma usual, pois configurações locais têm maior precedência que as globais.

É possível inclusive utilizar o booleanize com internacionalização. Coloque algo como isso no seu arquivo locale/blabla.yml

booleanize:
  true_string: Sim
  false_string: Não

E no seu environment.rb:

Booleanize::Config.default_strings :true => I18n.t("booleanize.true_string"), :false => I18n.t("booleanize.false_string")

E agora é possível instalar o booleanize como um plugin ou como uma gem (créditos para o José Valim). Basta fazer: sudo gem install cassiomarques-booleanize

Escrito por cassiomarques

Maio 25, 2009 em 2:28 am

Publicado em opensource, rails, ruby

Utilizando FakeWeb + Cucumber para testar autenticação por Oauth no Twitter

com 5 comentários

Continuando a série de posts sobre testes, neste artigo será mostrada uma técnica para simular o retorno das requisições HTTP realizadas durante o processo de autenticação por Oauth. Será utilizado como exemplo uma autenticação ao Twitter, mas a técnica é a mesma para qualquer esquema de autenticação que utilize Oauth.

O que é Oauth?

Se você ainda não sabe o que é o Oauth, dê uma olhada aqui. De forma resumida, o Oauth é um modelo de autenticação que pode ser utilizado quando uma aplicação A (consumer) precisa acessar dados de seus usuários em uma aplicação B (provider). Um exemplo que vem se tornando bem conhecido nos últimos tempos é a autenticação de aplicações que acessam o Twitter para ler e alterar dados de seus usuários. O fluxo é mais ou menos assim:

  • A aplicação A deve ser registrada na aplicação B. Ao final desse processo, será gerado um token e um secret, que serão utilizados para a criação de um consumer na aplicação A. A criação deste consumer é feita através deste par token/secret e da URL para a aplicação B.
  • Na aplicação A, quando quisermos autenticar um usuário na aplicação B, criamos um request token, a partir do consumer do passo anterior. Este request token também possui um par token/secret, que deve ser guardado de alguma forma. Em aplicações web ele geralmente é guardado na sessão do usuário.
  • O request token fornece também uma URL para autorização, para a qual o usuário da aplicação A deverá ser redirecionado. Essa URL pode também receber como argumento (é um request GET) uma URL de callback para alguma área da aplicação A ṕara o qual o usuário será retornado após ter se autenticado com sucesso na aplicação B.
  • Ao ser enviado para a aplicação B, o usuário informa seus dados de autenticação e, se estes estiverem corretos, é enviado de volta para a aplicação A. Ao retornar o usuário, a aplicação B também devolve o par token/secret previamente enviado.
  • Na aplicação A, o par token/secret recebido é comparado com os dados armazenados previamente na sessão do usuário. Se estes forem iguais, significa que o usuário foi corretamente autenticado na aplicação B. Isso também pode ser mecanismo de segurança.
  • Com o par token/secret recebidos, pode-se criar um novo request token e, a partir deste, conseguimos gerar um access token. Com o access token é possível realizar chamadas diretas à API da aplicação B, lendo e alterando dados ali armazenados para o usuário recém autenticado.

Porque testar isso é difícil

O problema em realizar testes funcionais em um processo de autenticação como esse é que o fluxo de execução não fica contido somente na nossa aplicação. Uma vez que o usuário é redirecionado para a aplicação provider, perdemos o controle do que está sendo executado. Considerando-se que precisamos recuperar o fluxo de execução quando o usuário for redirecionado de volta para nossa aplicação, pode-se ter idéia do tamanho do problema.

oauth-test

oauth-test

Além disso, se nossos testes forem fortemente acoplados à aplicação provider e realmente percorrerem o ciclo completo para a autenticação, alguns problemas poderão ocorrer:

  • Nossos testes serão lentos.
  • Não será possível testar caso a aplicação provider esteja indisponível.

O que precisamos fazer é enganar nossa aplicação. Precisamos fazer com que ela acredite estar enviando requisições para a aplicação provider, sem que isso aconteça de verdade. A primeira idéia que vem à mente é:

Se podemos usar o Rspec junto ao Cucumber, vamos criar mocks e stubs para simular os requests!

Hum… péssima idéia meu amigo. O motivo é simples:

Testes funcionais devem realmente testar sua aplicação. De ponta a ponta. Se você usar mocks e stubs, isso não será muito verdade… O propósito de ferramentas como o Cucumber é testar as coisas como elas realmente são.

Chegamos então a um paradoxo: Não podemos usar mocks e stubs, mas também não temos como testar se nossa aplicação realizar requests reais ao provider. A solução é uma gem muito interessante chamada FakeWeb.

FakeWeb

De forma resumida, o que o FakeWeb faz é acabar com o Net::HTTP. Destruir tudo o que ele faz, matar sua capacidade de realizar requests. Para quem não sabe, toda vez que fazemos um request HTTP a partir de nossa aplicação Rails, quem cuida de tudo é o Net::HTTP.

Como a coisa vai funcionar então? O FakeWeb permite registrar URLS e respectivos retornos. Todo o request feito pelo Net::HTTP será interceptado e a resposta “falsa” será retornada. Nossa aplicação nunca saberá a verdade!

Como saber o que retornar a partir dos requests registrados com o FakeWeb?

Boa pergunta! E a resposta é bastante interessante! Na verdade eu não inventei isso, me baseei no que li no blog Technical Pickles, e funciona muito bem!

Tudo o que temos a fazer é usar o curl e realizar um request a cada URL que nossa aplicação normalmente tentaria acessar durante o processo que pretendemos testar, direcionando a saída para um arquivo.

curl -is http://some.url > output-file

Com essa técnica podemos fazer com que o FakeWeb retorne o conteúdo deste arquivo quando for realizado um request para a URL registrada:

FakeWeb.register_uri('http://some.url', :response => File.join('path', 'to', 'output-file'))

Caso você não saiba exatamente quais são as URLs que deverão ser registradas, o FakeWeb possui uma configuração que faz com que qualquer request a uma URL não registrada (e que acessaria a rede) gere uma exceção contendo a URL solicitada. É uma forma suja de descobrir as URLs, mas resolveu meu problema :)

FakeWeb.allow_net_connect = false

Aplicando isso para testar o processo de autenticação ao Twitter com OAuth

Precisamos criar o código capaz de executar o seguinte step:

Given I am logged in

Estou considerando como exemplo a utilização da gem twitter-auth, a qual fornece mecanismos para autenticação ao Twitter usando OAuth ou autenticação básica.

No arquivo features/support/env.rb registramos as URLs no FakeWeb, para as quais já temos os retornos esperados dentro de arquivo gerados com o auxílio do curl:

require 'fake_web'
FakeWeb.allow_net_connect = false
FakeWeb.register_uri(:post, 'https://twitter.com:443/oauth/request_token', :string => 'oauth_token=faketoken&oauth_token_secret=faketokensecret')
FakeWeb.register_uri(:post, 'https://twitter.com:443/oauth/access_token', :string => 'oauth_token=fakeaccesstoken&oauth_token_secret=fakeaccesstokensecret')
FakeWeb.register_uri(:get, 'https://twitter.com:443/account/verify_credentials.json', :response => File.join(RAILS_ROOT, 'features', 'fixtures', 'credentials.json'))

Além disso, precisamos simular o redirecionamento realizado pelo provider de volta à nossa aplicação. Tomando como exemplo o código do controller utilizado no twitter-auth para criar as sessões:

class SessionsController < ApplicationController
  def new
    if TwitterAuth.oauth?
      @request_token = TwitterAuth.consumer.get_request_token
      session[:request_token] = @request_token.token
      session[:request_token_secret] = @request_token.secret

      url = @request_token.authorize_url
      url << "&oauth_callback=#{CGI.escape(TwitterAuth.oauth_callback)}" if TwitterAuth.oauth_callback?
      redirect_to url
    else
      # we don't have to do anything, it's just a simple form for HTTP basic!
    end
  end

  def oauth_callback
    unless session[:request_token] && session[:request_token_secret]
      authentication_failed('No authentication information was found in the session. Please try again.') and return
    end

   unless params[:oauth_token].blank? || session[:request_token] ==  params[:oauth_token]
     authentication_failed('Authentication information does not match session information. Please try again.') and return
   end

   @request_token = OAuth::RequestToken.new(TwitterAuth.consumer, session[:request_token], session[:request_token_secret])

   @access_token = @request_token.get_access_token

   #...

pode-se perceber que a primeira action acessada é new, a qual redireciona o usuário para realizar autenticação no Twitter. Quando o usuário é autenticado, ele é redirecionado de volta para a action oauth_callback do controlle SessionsController. Na action new são colocados na sessão o par token/secret de request, os quais serão confirmados quando o usuário voltar para a action oauth_callback. Será necessário realizar dois requests distintos na implementação do nosso step: um para colocar os dados na sessão e um segundo para simular o redirecionamento para a action oauth_callback.

O implementação do step ficaria mais ou menos assim:

Given /^I am logged in$/ do
  visit login_url
  visit oauth_callback_url
end

Tudo isso pra que duas linhas de código funcionem como esperado!

Escrito por cassiomarques

Abril 23, 2009 em 3:47 am

Publicado em rails, ruby, testes

A Cucumber step to generate any number of objects, for any model

com 4 comentários

A simple approach using Factory-Girl and some Ruby to prepare Cucumber scenarios with any number of objects, for any model

The Problem

When we write Cucumber scenarios, many times we’ll have to previously generate a certain number of records in the database. Then it becomes necessary to create several steps, each one capable to create objects for a specific model. It can be become very hard if we have many different models.

Given there are 3 products

Here we have ‘products’, which probably means we have a Product model and a step similar to this:

Given /^there are (\d+) products$/ do |n|
  n.to_i.times { Product.create(:description => 'foo', :price => 15.00) }
end

Now imagine writing this kind of code for several models. Also, imagine the hell it will be if you have several validates_uniqueness_of statements in these models…

The Solution

We need to create a generic way to create objects for our scenarios. With some help from Factory-Girl and some Ruby code, it can become very easy!

1 – Configuring Cucumber to use Factory-Girl

I’ve explained it here before, it’s simples!

  • Create a new folder to group all your factories. I use the same factories both for Cucumber and Rspec, so I put everything inside spec/factories. You can put it anywhere that makes sense for your project.
  • Inside this folder, create a file for each factory. It helps to keep everything organized. By convention, the name of each factory should be the name of the model, in singular, with camelCase replaced by underlines. For example, the definition of a factory for a ProductCategory model, inside a spec/factories/product_category_factory.rb file would look something like this

        Factory.define(:product_category) do |pc|
          # ...
        end
        
  • Configure Cucumber to include the factories when the scenarios are executed. We just need to add two lines of code in the features/support/env.rb file:

        require 'factory_girl'
        Dir.glob(File.join(File.dirname(__FILE__), '../../spec/factories/*.rb')).each {|f| require f }
        

2 -Creating our magic step definition

The idea is to always write our steps with the pluralized name of the model, with camelCase replaced by spaces. This helps to make everything more readable e takes advantage of the convention we defined for the factories’ names to easily the right factory to use to build our objects. Another detail is that if we specify that in our step there should be “x” objects for a certain model, then all the objects currently in the database must be deleted. Let’s see some code! This code can be defined in any file inside features/step_definitions:

Given /^there are (\d+) (.+)$/ do |n, model_str|
  model_str = model_str.gsub(/\s/, '_').singularize
  model_sym = model_str.to_sym
  klass = eval(model_str.camelize)
  klass.transaction do
    klass.destroy_all
    n.to_i.times do |i|
      Factory(model_sym)
    end
  end
  end

This code does the following:

  • Create any number of objects for any model.
  • Opens a transaction and delete all objects for this model from the database.
  • Creates n objects using Factory Girl (assumes there is a factory defined with a symbol which is the name of the model used in the scenario, with spaces replaced by underline and singular)

Now we can write steps like

Given there are 123 clients

or

Given there are 123 clients
And there are 7 product categories

and never worry with the definition for these steps again.

Escrito por cassiomarques

Abril 16, 2009 em 2:48 pm

Publicado em Desenvolvimento, rails, ruby, testes

Cucumber: Um step para criar qualquer número de objetos, para qualquer model

com um comentário

Uma simples técnica utilizando Factory-Girl e um pouco de Ruby para preparar cenários do Cucumber com qualquer quantidade de objetos, de qualquer model.

O problema

Quando escrevemos cenários com o Cucumber, muitas vezes é necessário criar um determinado número de registros no banco de dados para preparar um determinado cenário. Isso faz com que tenhamos que criar vários steps, cada um capaz de criar objetos para um determinado tipo de model. Isso pode se tornal realmente trabalhoso se tivermos muitos models diferentes.

Imagine um trecho de um cenário como este:

Given there are 3 products

Aqui temos ‘products’, que provavelmente exige a existência de um model Product e de um step parecido com

Given /^there are (\d+) products$/ do |n|
  n.to_i.times { Product.create(:description => 'foo', :price => 15.00) }
end

Agora imagine fazer esse tipo de coisa para vários models. Imagine também o inferno que isso se tornará se você tiver um monte de declarações do tipo validates_uniqueness_of nesses models…

A solução

Precisamos criar uma forma genérica de criar objetos nos nosso cenários. Com a ajuda do Factory-Girl e com um pouquinho de Ruby, a coisa fica fácil!

1 – Configurando o Cucumber para usar o Factory-Girl

Já expliquei isso aqui antes, é bem simples!

  • Crie uma pasta para guardar suas factories em algum lugar. Eu uso as mesmas factories tanto pro Cucumber quanto pro Rspec, então acabo deixando tudo em spec/factories. Coloque onde fizer mais sentido para o seu projeto.
  • Dentro dessa pasta, crie uma factory por arquivo. Isso ajuda a manter tudo organizado. Convencione o nome de cada factory como sendo o nome do model, no singular, com o camelCase substituído por underlines. Por exemplo, a definição de uma factory para o model ProductCategory, dentro de um arquivo em spec/factories/product_category_factory.rb ficaria mais ou menos assim:

        Factory.define(:product_category) do |pc|
          # ...
        end
        
  • Configure o Cucumber para incluir as factories quando os cenários forem executados. Basta adicionar duas linhas no arquivo features/support/env.rb:

        require 'factory_girl'
        Dir.glob(File.join(File.dirname(__FILE__), '../../spec/factories/*.rb')).each {|f| require f }
        

2 – Criando nossa definição de step mágico

A idéia é escrever os steps utilizando sempre o nome do model no plural, com camelCase substituído por espaços. Isso ajuda a deixar as coisas mais legíveis nos nossos cenários e se aproveita da convenção que criamos para o nome das factories para encontrar facilmente a factory a ser utilizada. Outro detalhe é que se especificarmos em nosso step que existem x objetos de um determinado model, todos os objetos previamente existentes deverão ser removidos. Vamos ao código, que pode ser definido em qualquer arquivo dentro de features/step_definitions:

Given /^there are (\d+) (.+)$/ do |n, model_str|
  model_str = model_str.gsub(/\s/, '_').singularize
  model_sym = model_str.to_sym
  klass = eval(model_str.camelize)
  klass.transaction do
    klass.destroy_all
    n.to_i.times do |i|
      Factory(model_sym)
    end
  end
  end

O que esse código faz é

  • Cria qualquer quantidade de instâncias de qualquer model.
  • Abre uma transação e apaga todos os objetos deste model do banco
  • Cria n objetos utilizando o Factory Girl (assume que existe uma factory definida com um symbol que é o nome do model passado no cenário, com os espaços substituídos por underlines e no singular)

Ou seja, agora podemos escrever steps como:

Given there are 123 clients

ou ainda

Given there are 123 clients
And there are 7 product categories

e nunca mais nos preocupar com as definições destes steps. Detalhe: Estamos usando as regras para pluralização em inglês. Se for programar em português, lembre-se de definir novas regras de pluralização no Inflector.

Escrito por cassiomarques

Abril 15, 2009 em 6:13 pm

Publicado em Desenvolvimento, rails, ruby, testes

ValeRuby – Grupo de Usuários Ruby do Vale do Paraíba

sem comentários

Acabamos de criar o site do ValeRuby, o grupo de usuários Ruby do Vale do Paraíba. A idéia é realizar encontros presenciais semanais, para falar sobre Ruby, desenvolvimento de software e outros assuntos geek. A participação é aberta para todos, residentes ou não no Vale do Paraíba.

Além do site, você também pode acompanhar nosso Twitter e se cadastrar na nossa lista de discussão

Vejo vocês nas próxima reunião!

Escrito por cassiomarques

Abril 9, 2009 em 5:30 am

Publicado em ValeRuby, ruby

Nova versão do plugin Booleanize: Agora recebendo Hashes!

sem comentários

Agora o plugin Booleanize também pode receber Hashes, além de Arrays e Symbols como parâmetros para o método booleanize.

class User  < ActiveRecord::Base
  booleanize :deleted => ["Yes, I'm gone", "No, I'm still here!"], :active => ["Yes!", "No"]
end

É possível também misturar os atributos

class User < ActiveRecord::Base
  booleanize :active, [:deleted, "Yes, I'm gone", "No, I'm still here!"], :smart => ["Smart!", "No, very dumb"]
end

O único cuidado é que o Hash deve ser o último parâmetro da lista, caso contrário você terá que colocá-lo entre chaves { … }

Agradeço ao Shairon Toledo que deu a idéia na lista do Rails-BR.

Escrito por cassiomarques

Dezembro 2, 2008 em 4:17 pm

Publicado em rails, ruby

Etiquetado com ,