Tagview Tecnologia

    Soluções web

    Browsing Posts in Ruby on Rails

    Recentemente, vi uma discussão no grupo de emails “rails-br” sobre como extender a classe String em uma aplicação Ruby on Rails. Na Tagview, costumamos utilizar muito este recurso em nossos projetos e acredito que fazemos isto de uma maneira bastante limpa e legível. Neste post vou detalhar este processo.

    Vou utilizar no exemplo um código que adiciona um método à classe String. Mas, gostaria de ressaltar que podemos extender qualquer classe do Ruby ou do Rails com este mesmo procedimento. Suponha que queremos adicionar um método de instância à classe String. Este método retornará a própria string, mas sem acentos. Vamos chamo-lo de “no_accent”. Lembrando que nem Ruby nem Rails implementam algum método que faça isto.

    Primeiramente, vamos definir um módulo que contenha este nosso método. Vamos chamá-lo de “StringExtensions”.

    module StringExtensions
      def no_accent
        self.
            gsub(/á/,  'a').     # à => a
            gsub(/á/,  'a').     # á => a
            gsub(/â/,  'a').     # â => a
            gsub(/ã/,  'a').     # ã => a
            gsub(/é/,  'e').     # é => e
            gsub(/ê/,  'e').     # ê => e
            gsub(/í/,  'i').       # í => i
            gsub(/ó/,  'o').     # ó => o
            gsub(/ô/,  'o').     # ô => o
            gsub(/ã/,  'o').     # õ => o
            gsub(/ú/,  'u').     # ú => u
            gsub(/ü/,  'u').     # ü => u
            gsub(/ç/,  'c').      # ç => c
            gsub(/À/,  'A').     # À => A
            gsub(/Á/,  'A').     # Á => A
            gsub(/Â/,  'A').     # Â => A
            gsub(/Ã/,  'A').     # Ã => A
            gsub(/É/,  'E').      # É => E
            gsub(/Ê/,  'E').      # Ê => E
            gsub(/Í/,  'I').        # Í => I
            gsub(/Ó/,  'O').     # Ó => O
            gsub(/Ô/,  'O').     # Ô => O
            gsub(/Õ/,  'O').     # Õ => O
            gsub(/Ú/,  'U').      # Ú => U
            gsub(/Ü/,  'U').      # Ü => U
            gsub(/Ç/,  'C')       # Ç => C
      end
    end
    

    Agora, basta incluir este módulo na classe String, certo? Sim, porém isto não é tão simples. O método de classe “include” é privado. Uma técnica interessante para burlar este problema é o uso do método “send” em cima do objeto que representa a classe String. Este método não é privado e aceita como argumentos: um método a ser chamado e os parametros a serem passados para este método. Assim, podemos incluir nosso módulo StringExtensions a classe String com o seguinte código:

    String.send :include, StringExtensions

    Para adicionar toda esta lógica à nossa aplicação basta criarmos um arquivo string_extensions.rb na pasta “config/initializers”. Neste arquivos adicionaremos nosso módulo e na última linha o código acima. Quando a aplicação for executada os arquivos “.rb” desta pasta serão executados em ordem alfabética e para toda string de nossa aplicação poderemos chamar o método “no_accent”.

    Esta é uma dica bastante simples porém muito útil. Extender classes pode ser um ótimo começo para um código mais clean, facil de manter e componentizável (imagine que em um próximo projeto RoR você pode reutilizar seus initializers).

    Abraços e até a próxima!

    Recentemente um dos nossos clientes nos relatou uma certa demora em algumas páginas importantes. Observando os relatórios do NewRelic, observei de cara uma query que demorava muito. Não foi difícil entender o motivo. Simplesmente faltava um índice na tabela. Um cuidado básico que nós desenvolvedores deveríamos estar atentos mas que muitas vezes acaba fugindo da nossa atenção. Assim que criei o índice, observei uma rapidez significante.

    Existe um plugin que nos ajuda muito nesta tarefa. É o rails_indexes . Ele dá um olhada na aplicação e sugere a criação de índices que ele julga serem importantes. Para instalá-lo, basta dar

    script/plugin install git://github.com/eladmeidar/rails_indexes.git

    Ele sugere um migração para a criação dos índices com o seguinte comando :

    rake db:index_migration

    O próprio autor do plugin diz que se trata de uma sugestão que deve ser analisada com cuidado pelo desenvolvedor ou pelo DBA, mas ele costuma revelar índices importantes que no calor do desenvolvimento, nós acabamos deixando para trás.

    Um método bem interessante do rails é o returning. Apesar de ser bem simples, ele ajuda a manter seu código mais limpo e mais fácil de entender (depois de pegar costume :P ).
    Funciona mais ou menos assim:

    Dado 2 argumentos, um objeto qualquer (arg1) e um bloco, o método repassa o objeto arg1 como parâmetro do bloco e ao final da execução do bloco, retorna o arg1 que pode ter sofrido alteração ou não.

    Por exemplo, o seguinte método:

    def published_titles(posts)
      titles = {}
    
      posts.each do |p|
        titles.merge!(p.id => p.title) if p.published?
      end
    
      titles
    end
    

    Poderia ser reescrito assim:

    def published_titles(posts)
      returning(Hash.new) do |titles|
        posts.each { |p| titles.merge!(p.id => p.title) if p.published? }
      end
    end
    

    É isso. ;)


    UPDATE
    O Ceolin comentou comigo hoje que o returning seria “deprecated” no Rails 3. Isso é porque a partir do Ruby 1.9, a classe Object ganha um novo método (tap) que permite o desenvolvedor interagir em uma chamada de métodos em cadeia, por exemplo:

    Users
      .all .tap {|users| puts "Usuários: #{users.map(&id)}"}
      .last.tap {|user| puts "Último usuário: #{user.name}"}
      .posts
    

    Então, o nosso primeiro método poderia ser escrito, no Ruby 1.9, assim:

    def published_titles(posts)
      Hash.new.tap do |titles|
        posts.each { |p| titles.merge!(p.id => p.title) if p.published? }
      end
    end