Linux, Open-source, Programação e Produtividade

Substituindo valores de um Hash em Ruby

convidados em 17/02/2009

Ruby Logo

Recentemente, ao criar utilizar o rspec para fazer BDD num projeto em Ruby on Rails, encontrei um problema que não aparentava ter uma solução elegante. Eu tinha um hash @valid_attributes com valores válidos para o meu modelo e gostaria de modificar apenas um deles para fazer um teste que quebrasse minha aplicação. A primeira solução possível foi:

it "should verify if name is not empty" do
  @valid_attributes[:name] = nil
  Account.new(@valid_attributes).should_not be_valid
end

Mas uma coisa me incomodou muito neste teste. Eu mudei a semântica da minha variável. Ela passou a conter um atributo inválido, mas continuava com o mesmo nome. Esta é uma péssima prática de programação, já que reutilizamos uma variável para outra coisa, perdendo o significado do nome. Portanto, apesar de parece inútil, o segundo passo foi:

it "should verify if name is not empty" do
  invalid_attributes = @valid_attributes
  invalid_attributes[:name] = nil
  Account.new(invalid_attributes).should_not be_valid
end

Mas agora o teste estava muito longo e parecia besteira o que estava sendo feito. Procurei na API do Ruby para tentar achar alguma solução elegante e com um pouco de trabalho cheguei a seguinte solução:

it "should verify if name is not empty" do
  Account.new(change_attribute(:name)).should_not be_valid
end

def change_attribute(attribute, value = nil)
  {attribute => value}.merge(@valid_attributes) {|k, o, n| o}
end

O método merge de um hash permite a fusão de um segundo hash ao primeiro, adicionando novos pares de chave – valor e substituindo os valores das chaves já existentes, mas se passarmos um bloco (como no exemplo acima) podemos escolher qual valor iremos utilizar para chaves repetidas. Indo um pouco além do usual, minha idéia foi substituir um par já existente – o que poderia ser feito para quantos pares fossem desejados. Espero que essa dica possa ser útil, já que o merge é um método muito interessante.