Blog da Improve It

MacRuby

Publicado por Vinicius Manhães Teles há 3 meses.

English version

Você gosta da interface gráfica dos Macs? Quem não gosta, né? Ela é um dos pontos altos da plataforma Macintosh, bem como do iPhone. Tudo viabilizado, em grande parte, por uma tecnologia chamada Cocoa.

No Mac, é possível criar interfaces gráficas em Cocoa, facilmente, utilizando o Interface Builder. Basta arrastar componentes de um lado para o outro, indicar ações e estabelecer conexões com o código. Ah sim, o código é implementado em Objective-C.

Objective-C é uma linguagem orientada a objetos que se aproxima bastante das características de Smalltalk. É uma linguagem com conceitos muito interessantes, porém bem verbosa. E pode representar uma barreira razoável para aqueles que gostariam de implementar aplicativos desktop no Mac, ou mesmo aplicativos no iPhone, especialmente se compararmos a outras linguagens como Ruby.

Pois Ruby é uma linguagem que foi feita com o objetivo de facilitar a vida do programador. É fácil, muito simples, extremamente expressiva e, de um modo geral, adorada por aqueles que a utilizam, incluindo eu mesmo. :-)

Então, de um lado temos uma tecnologia excepcional para interfaces gráficas, chamada Cocoa e de outro uma linguagem excelente chamada Ruby. Não seria ótimo se pudéssemos juntar as duas e ter o melhor dos dois mundos? Bem, isso já é possível há algum tempo usando, por exemplo, o RubyCocoa, que faz uma ponte entre Ruby e Objective-C. Entretanto, atualmente há uma opção ainda melhor chamada MacRuby.

MacRuby é uma versão do Ruby 1.9, portada para rodar diretamente sobre tecnologias que formam o core do Mac OS X tais como o runtime do Objective-C e o garbage collector, entre outros. A ideia é poder criar aplicações desktop no Mac OS X, sem sacrificar o desempenho, usando todos os benefícios do Ruby. Ainda está em desenvolvimento, mas o que está pronto já faz muito e o projeto como um todo é extremamente promissor.

Nestes últimos dias implementei uma pequena funcionalidade usando MacRuby e Cocoa. É algo útil para nosso dia a dia, embora bem básico, com o objetivo de configurar novos domínios DNS do Slicehost, que é onde hospedamos nossos sites. Irei disponibilizar o código no GitHub em breve.

Achei muito fácil fazer a implementação usando Cocoa e MacRuby, de modo que pude comprovar que o propósito do projeto MacRuby está sendo cumprido, ao menos naquilo que usei até o momento. Estou muito empolgado com tudo o que é possível fazer usando MacRuby, sobretudo com a agilidade e a facilidade de desenvolver dessa forma. Sendo assim, gostaria de recomendar aos interessados que deem uma olhada nele com carinho.

Vale dizer que eu já tinha algum conhecimento de Cocoa e Objective-C antes de fazer esse aplicativo. Minha principal referência tem sido o livro Cocoa Programming for Mac OS X.

Gostaria de agradecer ao Matt Aimonetti, que gentilmente me falou sobre o MacRuby, no Hora Extra, quando esteve aqui no Rio recentemente. O que, além de tudo, demonstra a importância de participar sempre do Hora Extra. :-)

E se você ainda não tiver se empolgado pelo MacRuby, aí vão algumas informações adicionais. O projeto é patrocinado pela própria Apple que tem grande interesse nele. De fato, o criador do projeto MacRuby, o belga Laurent Sansonetti trabalha na Apple e atualmente é pago pela empresa para trabalhar basicamente no MacRuby. Por que?

Porque em um futuro próximo, poderemos desenvolver aplicativos para o iPhone usando MacRuby! E isso abre espaço para que uma enorme leva de programadores Ruby comecem a produzir aplicativos para o iPhone. Portanto, se você ainda não programa em Ruby, talvez esteja aí o empurrãozinho que faltava. ;-)

Tags , , , ,  | 2 comentários

Migrando o Brazilian Rails para Ruby 1.9

Publicado por Marcos Tapajós há 6 meses.

Algumas semanas atrás Fernando Luizão migrou o Brazilian Rails para funcionar no Ruby 1.9 mantendo a compatibilidade com o Ruby 1.8.X. Estou para divulgar isso tem alguns dias porém eu não tive muito tempo.

Ele explicou em detalhes no blog dele tudo que foi necessário fazer.

Deixo aqui registrado meu muito obrigado ao Fernando.

Tags , , ,  | nenhum comentário

Dev in Rio 2009

Publicado por Vinicius Manhães Teles há 7 meses.

Dev in rio 2009

No dia 14 de setembro de 2009 você tem um compromisso. Chama-se Dev in Rio 2009. Uma conferência super bacana que vai dar o que falar.

Será um evento raro, pois não é sempre que várias tecnologias diferentes dividem o mesmo palco. Teremos o privilégio de assistir:

A abertura será feita pelos meus amigos, Guilherme Chapiewski e Henrique Bastos, os organizadores do evento. Grandes amigos, dos quais me orgulho muito e aos quais agradeço por trazer um evento tão legal para o Rio. Parabéns por essa brilhante iniciativa!

Também estarei presente, no final do dia, para ajudar a organizar um bate-papo entre os palestrantes e a comunidade de desenvolvimento de software presente ao evento.

Dentre os assuntos tratados, temos o Joomla!, Java, Ruby, Ruby on Rails, Python, Django e Desenvolvimento Ágil. E como se não bastasse, o evento será encerrado com chave de ouro, em uma #horaextra que certamente vai entrar para a história. :-)

Veja informações adicionais sobre o evento nos blogs dos organizadores:

Palestrante Guilherme Chapiewski
http://gc.blog.br

Palestrante Henrique Bastos
http://henriquebastos.net

Faça logo sua inscrição! Este vai ser um daqueles eventos em que as vagas vão acabar rapidinho. Pode ter certeza.

Fiquem ligados no evento, acompanhando todos os acontecimentos no Twitter, seguindo o @devinrio e a busca #devinrio! Vejo vocês lá!

Tags , , , , , , , , , ,  | 1 comentário

Como Testar parte 2 - Mocks

Publicado por Marcos Tapajós há aproximadamente 1 ano.

Tem gente "chutando" Demeter.

Minha idéia era escrever um post dessa série por semana, mas infelizmente uma tendinite tem me atacado e está meio complicado ficar escrevendo muito. Por isso mesmo esse segundo artigo será bem compacto. Todos os códigos citados nesse post fazem parte de um projeto How Test que está disponível em: http://github.com/tapajos/how-test

Nesse post a minha idéia é mostrar de forma bem simples como funciona um mock object no Rspec e no Test::Unit(usando o mocha).

Para exemplificar eu vou fazer uma crítica a uma construção que eu tenho visto muito em diversos projetos e que viola a Lei de Demeter(Principle of Least Knowledge). Ou como dizia, o meu amigo, Bernardo, "Tem gente chutando Demeter!".

Vamos a um exemplo dessa violação:

Supondo que você tenha um modelo Account que se relaciona ao modelo User.

class Account < ActiveRecord::Base
  belongs_to :user
end

Freqüentemente eu vejo construções do tipo:

@account.user.name
@account.user.mail
@account.user.rg.number
@account.user.rg.state
@account.user.rg.city

O grande problema é que nesse tipo de construção você está "conhecendo" coisas demais e certamente vai pagar por isso num futuro breve, quando você precisar fazer um refactoring e tiver que mudar em vários lugares. Imagina se o User deixa de ter um "name" e passa a ter um "full_name".

Para resolver esse tipo de problema basta você concentrar esse "conhecimento" no seu modelo Account da seguinte forma:

class Account < ActiveRecord::Base
  belongs_to :user
  def user_name
    user.name
  end
end

OBS: Vou me concentrar apenas no problema com o nome e não vou me preocupar em validar se o relacionamento foi estabelecido.

Bem, finalizada a crítica a uma falha de design OO vamos ao objetivo desse post, mostrar como *EU* testaria esse problema.

Como eu falei no post anterior, não gosto muito de usar fixtures para testes unitários e por isso mesmo vou apelar aos mock objects. Se você não está muito familiarizado com mocks sugiro que pare por aqui e leia um pouco mais sobre isso. Uma referência rápida pode ser o wikipedia mas realmente sugiro que vá mais adiante.

Testando usando RSpec(usando o framework de mock padrão):

before(:each) do
  @account = Account.new    
  @user_mock = mock_model(User)
  @account.stub!(:user).and_return(@user_mock)
end

describe ".user_name" do

  it "should delegate to user.name" do
    @user_mock.should_receive(:name).and_return("Tapajós")
    @account.user_name.should == "Tapajós"
  end

end

Testando usando Test::Unit com mocha:

setup :create_model

def test_user_name
  @user_mock.expects(:name).returns("Tapajós")
  assert_equal "Tapajós", @account.user_name
end

private

  def create_model
    @account = Account.new
    @user_mock = mock("User")
    @account.stubs(:user).returns(@user_mock)
  end

Explicando...

Em ambos os casos eu preciso que a minha account simule o relacionamento com User e para isso eu vou retornar um mock object. Isso é feito pelas linhas:

RSpec:

@account.stub!(:user).and_return(@user_mock)

Test::Unit:

@account.stubs(:user).returns(@user_mock)

Após o setup ou o before, temos um modelo @account onde o @account.user retorna um mock.

Feito isso eu preciso configurar o meu mock, isto é, informar que ele receberá uma mensagem name (chamada do método .name) e essa retornará o meu nome(sim, sou egocêntrico). Isso é feito pelos métodos "should_receive" e "expects" conforme as linhas abaixo:

RSpec:

@user_mock.should_receive(:name).and_return("Tapajós")

Test::Unit:

@user_mock.expects(:name).returns("Tapajós")

Depois que nossos mocks foram devidamente configurados podemos, finalmente, fazer nossa verificação do retorno, isto é, simplesmente chamar nossos métodos conferir o retorno. Pronto teste feito com sucesso, sem precisar recorrer a banco de dados nem configurar fixtures.

Nesse momento deve ter surgido uma dúvida: "Porque uma hora você usa stub! e outra um should_receive?"

A resposta é bem simples, o stub! não faz um verify no final enquanto a outra chamada sim. Na pratica isso significa:

Stub! ou stubs

Quando eu uso stub!(ou um Stubs) eu estou configurando o meu modelo account para responder pelo método user retornando o @mock_user porém não me interessa quebrar esse teste caso você não chame esse método.

Should_receive ou expects

Quando eu uso should_receive(ou um expects) eu estou configurando o meu mock user para responder pelo método name porém caso esse método não seja chamado eu devo quebrar meu teste, pois isso seria um comportamento indesejável.

Porque usar mocks?

Ao contrário do que muita gente pensa o uso de mocks não é um bicho de 7 cabeças, é bem simples. Na verdade testar é algo simples, desde que você tenha domínio do ferramental e os mocks são realmente úteis em diversos casos.

Imagina que o seu sistema precise fazer consultas a uma api publica do Yahoo e para isso você tenha criado uma classe de consultas. Você não vai querer(nem o Yahoo vai gostar) ir lá no servidor toda vez que você rodar os seus testes. Isso tornaria os seus testes lentos e impossível roda-los offline. Nesse caso você resolve seu problema "mockando" essa classe.

Tags , , , , , ,  | 8 comentários

Como Testar parte 1 - Models

Publicado por Marcos Tapajós há aproximadamente 1 ano.

Quando comecei a estudar Extreme Programming descobri que não é possível fazer nenhum software de qualidade sem uma excelente base de testes. Desde então tenho me dedicado muito ao estudo das mais diversas ferramentas e técnicas para elaborar bons de testes.

O assunto testes é bastante polêmico e não pretendo (nesse post) tentar convencer ninguém da importância deles. Se você não faz testes e/ou discorda de qualquer uma das minhas afirmações deixo algumas perguntas para você refletir.

1 - Quantos bugs fixes você fez esse ano?

2 - Quantos tickets abertos existem no seu bug tracker?

3 - Quantas vezes você fez um deploy de uma nova versão em uma sexta feira de tarde e saiu mais cedo do trabalho?

4 - Quantas vezes você "virou a noite" esse ano?

Acabei me tornando um evangelizador de testes porém nunca fiz nada muito prático para passar o conhecimento que eu adquiri para a comunidade. Só que agora vou me redimir dessa falha iniciando uma série de posts onde vou expor um problema e como EU testaria usando Test::Unit e Rspec. Não vou falar de Shoulda pois não gosto dele. :-)

A idéia de escrever essa série de posts sobre testes surgiu logo após a gravação do terceiro episódio do RailsBox e gostaria de agradecer ao Ozeias e ao Davis Cabral por terem me motivado.

"Back to the cold cow..."

O ActiveRecord simplifica muito nossos modelos porém tenho observado que em vários projetos os desenvolvedores deixam de testar corretamente os seus modelos usando a alegação que não vão testar alguma coisa que o Rails já testou. Esse é um argumento valido em alguns casos pois você está apenas delegando responsabilidades mas você sempre deve testar se a responsabilidade foi realmente delegada.

Um exemplo clássico são as validações. Teoricamente você não precisaria testar como elas são implementadas mas deve testar se elas realmente existem pois se alguém remove-las seus testes vão continuar passando mas sua aplicação estará quebrada e/ou permitindo inconsistências de banco de dados.

Nesse post vou mostrar como testar alguns comportamento do ActiveRecord usando como base o modelo User. Todos os códigos citados nesse post fazem parte de um projeto How Test que está disponível em: http://github.com/tapajos/how-test

class User < ActiveRecord::Base
  validates_presence_of :name
  validates_format_of :mail, 
                      :with => /([-.\w^@]+@(?:[-\w]+.)+[A-Za-z]{2,4})+/i,
                      :on => :create,
                      :allow_nil => true
  has_many :accounts
  named_scope :actives, :conditions => ["active = ?", true]
end

Uma boa estratégia para orientar o desenvolvimento dos teste é elaborar algumas perguntas que darão origem aos seus cenários de testes.

Validação do nome

  1. Posso cadastrar um usuário sem nome? Não

Testando usando Test:Unit:

def test_if_check_presence_of_name
  assert !@user.valid?, "Should be invalid"
  assert_equal "can't be blank", @user.errors[:name]
end

Na primeira linha desse teste o assert recebe um segundo parâmetro que por ser opcional não é muito comentado mas merece uma atenção especial. Esse argumento nada mais é do que a mensagem que será exibida quando o teste quebrar. Quando você omite esse parâmetro o teste quebra exibindo a mensagem 'false is not true' que não ajuda muito a entender o que está acontecendo.

Testando usando RSpec:

it "should reject if name is not given" do
  @user.should have(1).error_on(:name)
  @user.errors[:name].should == "can't be blank"
end

Validação do e-mail.

  1. Posso criar um registro com um e-mail inválido? Não
  2. Posso atualizar um regitro com um e-mail inválido? Sim
  3. Posso criar um registro com um e-mail em branco? Sim

Testando usando Test:Unit:

VALIDS_MAIL = %w(foo@bar.com foo@bar.com.br foo@globo.com foo@i_hate_the_microsoft.com foo@i_love_my_mac.com)
INVALIDS_MAIL = %w(foobar.com foo@bar i_hate_the_microsoft.com i_love_my_mac.com)

def test_if_reject_invalid_format_os_mail_on_create
  INVALIDS_MAIL.each do |mail|
    @user.mail = mail
    assert !@user.valid?, "Should be invalid when mail is #{mail}"
    assert_equal "is invalid", @user.errors[:mail]
  end
end

def test_if_not_reject_when_mail_is_nil
  @user.name = "Tapajós"
  assert @user.valid?, "Should be valid"
end

def test_if_not_check_format_of_mail_on_update
  @user.name = "Tapajós"
  assert @user.save, "Should save"
  @user.mail = "an invalid mail"
  assert @user.valid?, "Should be valid"
end

def test_if_accept_a_valid_mail
  VALIDS_MAIL.each do |mail|
    @user.name = "Tapajós"
    @user.mail = mail
    assert @user.valid?, "Should be valid when mail is #{mail}"
  end
end

Testando usando RSpec:

INVALIDS_MAIL.each do |mail|
  it "should reject because #{mail} is an invalid mail" do
    @user.mail = mail
    @user.should have(1).error_on(:mail)
    @user.errors[:mail].should == "is invalid"
  end
end

VALIDS_MAIL.each do |mail|
  it "should be valid when mail is #{mail}" do
    @user.mail = mail
    @user.should_not have(1).error_on(:mail)
  end
end

it "should not reject if mail is not given" do
  @user.name = "Tapajós"
  @user.should be_valid
end

it "should not check mail format on update" do
    @user.name = "Tapajós"
    @user.save.should be_true
    @user.mail = "an invalid mail"
    @user.should be_valid
end

Testando o relacionamento com Account.

  1. Um usuário pode ter mais de uma conta? Sim

Testando usando Test::Unit:

def test_has_many_accounts
  association = User.reflect_on_association(:accounts)
  assert association, "Association with account is not found"
  assert_equal :has_many, association.macro
end

Testando usando RSpec:

it "should has many accounts" do
  association = User.reflect_on_association(:accounts)
  association.should_not be_nil
  association.macro.should == :has_many
end

Testando o User.actives

  1. Posso listar usuários inativos? Não

Testando usando Test::Unit:

def test_if_actives_use_the_correct_conditions
  assert_equal({:conditions=>["active = ?", true]}, User.actives.proxy_options)
end

Testando usando RSpec:

it "should find for all users that status of active is true" do
  User.actives.proxy_options.should == {:conditions=>["active = ?", true]}
end

Para esse post ficar mais simples e curto não me preocupei em validar se o tamanho máximo dos campos está coerente com o tamanho máximo permitido pelo tipo no banco de dados. Essa é uma validação EXTREMAMENTE importante que não deve ser esquecida!

No próximo post dessa série falarei um pouco sobre a Lei de Demeter, como respeita-la e testar alguns métodos usando Mock Objects.

O que você achou desse artigo? O que você gostaria de saber sobre testes de ActiveRecord que eu não falei aqui?

Aguardo o feedback de vocês.

Tags , , , , , ,  | 46 comentários

Mais uma gem no Brazilian Rails

Publicado por Marcos Tapajós há aproximadamente 1 ano.

O Brazilian Rails acabou de ganhar uma gem para lidar com CPF e CNPJ desenvolvida pelo Cássio Marques. Ele fez um post no seu blog explicando como utilizar.

http://tinyurl.com/cpfcnpj

Além dessa nova gem a versão 2.0.8 do Brazilian Rails recebeu algumas pequenas correções feitas pelo Akita e pelo Sergio Brant.

Gostaria de agradecer aos três pelas colaborações.

Tags , , , , ,  | nenhum comentário

Integration Plugin com GIT

Publicado por Marcos Tapajós há aproximadamente 1 ano.

Agora o Integration Plugin suporta GIT!

intplugin

Desde o lançamento do Integration Plugin eu tinha a sensação que faltava o suporte ao GIT porém como o plugin foi extraído dos nossos códigos e não usávamos esse SCM ele foi lançado assim mesmo.

Logo depois o Eduardo Fiorezi me enviou um patch adicionando esse suporte e posteriormente o suporte ao git-svn só que eu não aceitei de imediato pois faltava documentar e eu estava completamente sem tempo para isso. Só que hoje recebi um patch do Sylvestre Mergulhão atualizando a documentação.

Obrigado aos dois!

Tags , , ,  | 4 comentários

Brazilian Rails no github

Publicado por Marcos Tapajós há aproximadamente 1 ano.

Acabei de terminar a migração do plugin Brazilian Rails para o github. Achei que ia ser bem mais complicado do que foi ! :-)

Nós decidimos manter o SVN como um espelho (por um tempo) para facilitar a instalação nas versões anteriores a Rails Edge. Agora existem duas formas de instalação que estão detalhadas na página do plugin.

Para quem quiser acompanhar o projeto a url no github é:

http://github.com/tapajos/brazilian-rails

Tags , , , , ,  | nenhum comentário

Nova Página para o Brazilian Rails

Publicado por Marcos Tapajós há aproximadamente 1 ano.

Hoje o Brazilian Rails ganhou um site um pouco mais bonitinho com um design igual ao do nosso outro plugin.

Na verdade esse é um template, de autoria do Leandro, que será usado em todos os nossos plugins. Só não está mais bonito pois resolvi meter a mão e me antecipar ao Leandro.

Tags , , ,  | nenhum comentário

Brazilian Rails: Patch aceito.

Publicado por Marcos Tapajós há aproximadamente 1 ano.

Como sempre estou atrasado e só hoje vi alguns patch no Brazilian Rails.

Patch #17896 - usar_como_dinheiro não funciona com audit.

O Sylvestre Mergulhão identificou uma incompatibilidade do plugin com o audit que não fazia log das colunas mapeadas com o usar_como_dinheiro. O patch que ele enviou já foi aceito.

Patch #18822 - Criação de metodos para que o select_estado funcione com o form_for.

Esse patch foi enviado pelo Rafael Cardoso e é bastante pertinente só que não foi aceito ainda pois o patch foi gerado de forma incorreta. Ele já fez contato comigo e vai enviar o patch correto em breve.

Queria agradecer aos dois pela colaboração constante. Já perdi a conta de quantas vezes aceitei código deles.

Tags , , ,  | nenhum comentário

Artigos antigos: 1 2 3 4