RSS

Refactoring: Ruby Edition, eu quero!

September 23rd, 2008 by Eduardo Fiorezi

Jay Fields anunciou em seu blog que o livro Refactoring: Ruby Edition está a venda na loja virtual Safari. Para quem já leu a versão antiga em Java, esta obra dispensa apresentações, eu recomendo a todos que gostam de código bem escrito.

Os autores dos livros são Jay Fields, Shane Harvie, Martin Fowler e com a participação de Kent Beck.

Posted in Ruby, livros, Refactoring, TDD, Geral | 1 Comment »

Extraindo um padrão para seus forms no Rails

November 11th, 2007 by Eduardo Fiorezi

Imagine que você tenha um padrão para seus forms, como o gerado pelo scaffold_resources:

<% form_for(:podcast, :url => podcast_path(@podcast), :html => { :method => :put }) do |f| %>
 
  <p>
    <b>Title</b><br />
    <%= f.text_field :title %>
  </p>
 
  <p>
    <b>Created by</b><br />
    <%= f.text_field :created_by %>
  </p>
 
  <p>
    <%= submit_tag "Update" %>
  </p>

Você deve criar uma nova classe que extende de ActionView::Helpers::FormBuilder, sobrescrevendo os campos que você quer, segue logo abaixo uma implementação para todos os campos, exceto o hidden_field:

class DefaultFormBuilder < ActionView::Helpers::FormBuilder
  (field_helpers - %w(hidden_field)).each do |selector|
    src = <<-END_SRC
      def #{selector}(field, options = {})
        @template.content_tag("p" ,
        @template.content_tag("b" , field.to_s.humanize) + "<br />" + super)
      end
    END_SRC
    class_eval src, __FILE__, __LINE__
  end
end

A aplicação é muito simples, veja:

<% form_for(:podcast, :url => podcast_path(@podcast), :html => { :method => :put },
                   :builder => DefaultFormBuilder) do |f| %>
 
    <%= f.text_field :title %>
    <%= f.text_field :created_by %>
 
  <p>
    <%= submit_tag "Update" %>
  </p>

O código fica mais limpo e padronizado.

Mas agora eu quero mudar o título que aparece entre a tag bold, posso então inventar uma nova propriedade para o text_field:

<% form_for(:podcast, :url => podcast_path(@podcast), :html => { :method => :put },
                   :builder => DefaultFormBuilder) do |f| %>
 
    <%= f.text_field :title,     :label => "Título" %>
    <%= f.text_field :created_by, :label => "Criado por" %>
 
  <p>
    <%= submit_tag "Update" %>
  </p>

A modificação é simples:

class DefaultFormBuilder < ActionView::Helpers::FormBuilder
  (field_helpers - %w(hidden_field)).each do |selector|
    src = <<-END_SRC
      def #{selector}(field, options = {})
        @template.content_tag("p" ,
        @template.content_tag("b" ,(options[:label] || field.to_s.humanize)) + "<br />" + super)
      end
    END_SRC
    class_eval src, __FILE__, __LINE__
  end
end

Pensamento interior: “Como é bom apagar código. ;)”

Posted in Ruby, Refactoring, Rails | 2 Comments »

RejectConf SP’07 - Refactoring on Rails

October 24th, 2007 by Eduardo Fiorezi

O Akita publicou a lista de palestras confirmadas até o momento. O Brasil possui cerca de 250 railers cadastrados no working with rails e já estava na hora de acontecer um evento voltado para os desenvolvedores, onde a própria comunidade pudesse mostrar suas experiências.

Pretendo falar sobre refactoring, sua importância e como ele é fundamental em qualquer projeto de desenvolvimento de software, inclusive em projetos ruby/ruby on rails. Já estou com algumas idéias interessantes para não deixar ninguém dormir na cadeira.

Espero poder conversar pessoalmente com todos blogueiros que assino e aqueles que entrevistei nos meus podcasts.

Deixando a dica aqui de vários outros encontros que vão acontecer no Brasil: Rio on Rails, RS, MG, SC, PE, online pelo treinatom e Brasilia.

Posted in Refactoring, Rails, Geral | 2 Comments »

Authenticação e assert_difference.

September 2nd, 2007 by Eduardo Fiorezi

Nas minhas aplicações Rails eu costumo utilizar o plugin “Restful Authentication” que disponibiliza um código inicial para criar usuários e o esqueleto de autenticação. (Veja como utilizar)

O ponto principal na escolha deste plugin é a alta cobertura de testes de unidade e funcionais, assim posso inserir novas funcionalidades com segurança e mantendo a cobertura de testes.

Os testes gerados pelo plugin utilizam o helper assert_difference e o assert_no_difference presente somente no “edge rails”. Felizmente o plugin sofreu algumas alterações e já possui no seu helper de testes esses dois métodos. Então se você utilizou o plugin antes dessa modificação, ou já quer usar este helper antes do Rails 2.0 ser lançado, basta adicionar o seguinte código no test_helper:

  def assert_difference(expressions, difference = 1, message = nil, &block)
    expression_evaluations = [expressions].flatten.collect{|expression| lambda { eval(expression, block.binding) } } 
 
    original_values = expression_evaluations.inject([]) { |memo, expression| memo << expression.call }
    yield
    expression_evaluations.each_with_index do |expression, i|
      assert_equal original_values[i] + difference, expression.call, message
    end
  end
 
  def assert_no_difference(expressions, message = nil, &block)
    assert_difference expressions, 0, message, &block
  end

Sua utilização é simples, se antes você tinha esse código:

  def test_create_player
    old_count = Player.count
    post :create, :player => {:name => "Eduardo", :game => "Starcraft 2"}
    assert_response :redirect    
    assert_equal old_count + 1, Player.count
  end

Você agora pode utilizar este que é muito mais bonito:

  def test_create_player
    assert_difference 'Player.count' do
      post :create, :player => {:name => "Eduardo", :game => "Starcraft 2"}
      assert_response :redirect
    end
  end

O Artur publicou um artigo com mais alguns helpers que ele utiliza.

Fica ai a dica.

Posted in Ruby, Refactoring, Rails, TDD | 2 Comments »

Refactoring para unir duas actions e responder como eu quero.

July 1st, 2007 by Eduardo Fiorezi

Quando comecei a escrever código em Rails, algumas coisas ainda não estavam tão claras na minha cabeça, principalmente como o RESTful funciona. Este artigo irá mostrar como unificar 2 metodos do meu controller que fazem a mesma coisa, para responder de maneiras diferentes. Este código está na minha aplicação de podcasts. Lembre-se de ter testes no seu código, vou suprimi-los aqui, sem eles sua refatoração é duvidosa.

1) Qual é a idéia?

Eu precisava hospedar meus arquivos mp3 em outro servidor, pois tenho pouco espaço para isso. Mas e se eu trocasse de lugar os arquivos? Eu teria sempre que quebrar os links de mp3 do meu sistema. Então eu guardo no banco o link onde está o meu podcast(o ouvinte não precisa saber disso rs).
As duas actions a seguir refletem o seguinte comportamento:

Quando o usuário entra em http://podcasts.tudoquequerosaber.com/podcast/Pod7-IvanSanchez ele é enviado para a action show, que irá mostrar os comentários do Podcast.

E para fazer o download ou escutar o podcast é so adicionar .mp3 ao final do link http://podcasts.tudoquequerosaber.com/podcast/Pod7-IvanSanchez.mp3, não importa onde está o arquivo, o sistema tem que mandar o mp3. Esta requisição envia o params[:page_name] = Pod7-IvanSanchez para a action download pedindo uma requisição “audio/mpeg”. Você pode usar essa URL no seu player de mp3.

Esta é a implementação inicial, bem feia por sinal:

  def download
    @podcast = Podcast.find_by_urlname(params[:page_name])
 
    unless @podcast.nil? 
     respond_to do |format|
        format.mp3 do          
          Counter.create(:podcast_id => @podcast.id)
          redirect_to @podcast.link
        end
      end
    else
      redirect_to :action => 'index'
    end
  end
 
  def show
    @podcast = Podcast.find_by_urlname(params[:page_name])
 
    if @podcast.nil?
      redirect_to :action => "index"
    end
  end

Isto nao está nada bom, e as coisas não podem ficar feias assim com Ruby/Rails. Vamos uni-las na action show, fazendo que action possa responder como mp3 e html.

  def show
    @podcast = Podcast.find_by_urlname(params[:page_name])
 
    unless @podcast.nil? 
     respond_to do |format|
        format.html #<<<<<<<<<<<<<<<<<<<<
        format.mp3 do          
          Counter.create(:podcast_id => @podcast.id)
          redirect_to @podcast.link
        end
      end
    else
      redirect_to :action => 'index'
    end
  end

2) O que faz o método find_by_

Acho que todos já sabem que o find_by_nome_do_campo_da_tabela, utiliza o method missing do ruby, mas ele tem um comportamento bem mais funcional.

Fazer um:

Podcast.find_by_urlname(params[:page_name])

é a mesma coisa que utilizar:

Podcast.find(:first, :conditions => ["urlname = ?", params[:page_name])

caso esta pesquisa não retorne nada, ganhamos um nil de presente. Posso deixar meu codigo mais legível:

  def show
    @podcast = Podcast.find_by_urlname(params[:page_name])
 
    if @podcast #<<<<<<<<<<<<<<<<<<<<
     respond_to do |format|
        format.html
        format.mp3 do          
          Counter.create(:podcast_id => @podcast.id)
          redirect_to @podcast.link
        end
      end
    else
      redirect_to :action => 'index'
    end
  end

3) Refactoring Extrair método

Posso deixar meu método show ainda mais legível, vamos extrair todo comportamento do format.mp3 dentro do respond_to, é uma das refatorações mais utilizadas do livro de Martin Fowler Refactoring: Improving the Design of Existing Code. Vamos lá novamente:

  def show
    @podcast = Podcast.find_by_urlname(params[:page_name])
 
    if @podcast
      respond_to do |format|
        format.html
        format.mp3 {add_counter_and_redirect_to_mp3(@podcast)}
      end
    else
      redirect_to :action => "index" 
    end                          
  end
 
  private
 
  def add_counter_and_redirect_to_mp3(podcast)
    Counter.create(:podcast_id => podcast.id)
    redirect_to podcast.link
  end

A action show é uma das 7 que o restful do Rails nos entrega. Agora posso partir para este novo passo. Mas fica para a próxima. Todos esses passos foram garantidos pelos testes funcionais. Não esqueçam deles. ;)

Deixe seu comentário.

Posted in Refactoring, Rails | 1 Comment »