Filter Table: Plugin para geração de filtros para Rails

Filter Table é um plugin para Ruby on Rails que possibilita uma forma simples para filtragem de dados. O plugin adiciona um método ao controller para gerar condições do ActiveRecord baseado nos parâmetros passados na requisição. Além disso, o plugin inclui Helpers para auxiliar a inserção dos links de filtros nas Views.

Instalando:

O código do plugin está hospedado no GitHub, para instalá-lo em sua aplicação, execute o comando:

script/plugin install git://github.com/flaviogranero/filter_table.git

Usando:

Para usar o plugin, basta adicionar uma chamada ao método filter_attributes no controller desejado, passando os atributos que podem ser usados na construção do filtro. Feito isso, pode-se chamar o método filter_conditions na carga de dados do seu modelo. Vejamos um exemplo de uso no controller:

Controller (cars_controller.rb):


class CarsController < ApplicationController
    filter_attributes :status

    def index
      @cars = Car.paginate :page => params[:page], :conditions => filter_conditions
    end
  end

Observe que o modelo Car possui um atributo “status” do tipo inteiro, que será usado pelo plugin para gerar as condições baseado nos valores passados como parâmetros da requisição. Um exemplo de URL filtrando os carros com status igual a 1 por exemplo, seria: /cars?status=1

Os filtros ainda podem ser combinados. Um exemplo para isso seria uma requisição para mostrar os carros com status igual a 1 OU a 2: /cars?status=1+2

Criar links na sua página de listagem com todas as combinações de filtros pode ser um tanto trabalhoso. Para facilitar isso, o plugin conta com método auxiliar com essa finalidade, chamado filter_links_for. Os parâmetros para o método variam de acordo com a necessidade e o tipo do atributo. Vejamos algums exemplos de views.

View (index.html.erb):


<h1>Cars</h1>
  <div>
    <h2>Filters</h2>
    < %= filter_links_for :status, :values => {'Normal' => 0, 'Broken' => 1, 'Running away' => 2} %>
  </div>
  <table>
    <tr>
      <th>Name</th>
      <th>Status</th>
      <th>Category</th>
    </tr>
    < % @cars.each do |car| %>
      <tr>
        <td>< %=h car.name %></td>
        <td>< %= car.status %></td>
        <td>< %= car.category %></td>
      </tr>
    < % end %>
  </table>

No exemplo de view acima, temos como parâmetro o atributo status, e um Hash com os captions e valores possíveis na chave :values.

Você pode ainda passar alguns parâmetros opcionais, como o título da lista de links que será gerada, usando a chave :title, e a classe css usada em filtros ativos pela chave :active_class, cujo valor padrão é “filter_active”.

O parâmetro :values aceita Hashs, Arrays ou ainda o valor :auto. Neste último caso, os links dos filtros são geradas a partir de uma query no banco de dados por valores distintos do atributo em questão. Para que isso seja possível, é necessário o uso da chave :model associada ao nome do Modelo, como pode ser observado no exemplo abaixo, para o atributo category:

View (index.html.erb):


<h1>Cars</h1>
  <div>
    <h2>Filters</h2>
    < %= filter_links_for :status, :values => {'Normal' => 0, 'Broken' => 1, 'Running away' => 2} %>
    < %= filter_links_for :category, :values => :auto, :model => 'Car' %>
  </div>
  <table>
    <tr>
      <th>Name</th>
      <th>Status</th>
      <th>Category</th>
    </tr>
    < % @cars.each do |car| %>
      <tr>
        <td>< %=h car.name %></td>
        <td>< %= car.status %></td>
        <td>< %= car.category %></td>
      </tr>
    < % end %>
  </table>

Para cada valor de filtro, é gerado um link para filtragem exclusiva, e outro para adicionar o valor ao filtro atual. Quando um filtro já está aplicado, esse link é substituído por outro, com a função de remover o valor do filtro atual.

Contribuindo:

Contribuições com idéias e novas funcionalidades são sempre bem vindas. O plugin possui cobertura por testes unitários, bastando executar “Rake test” dentro de sua pasta. Fique ä vontade para clonar o repositório em

http://github.com/flaviogranero/filter_table/

Conclusão:

O uso do plugin filter_table torna páginas com grandes listas de dados, principalmente as usadas para administração de sistemas, mais fáceis de navegar e de visualizar.

Criei o plugin para sanar a necessidade por filtros que tinha em páginas administrativas de um sistema em Ruby on Rails, já que as soluções em plugins e gems existentes com a solução são normalmente complexos de configurar e manter, agregam muitas funcionalidades e na maioria das vezes fazem uso de AJAX de forma abusiva.

O filter_table destina-se a resolver apenas o problema dos filtros, sem mágica, mantendo o código simples.

Ilustrações: Camisetas de Carnaval

Criar a ilustração da camiseta do bloco de carnaval “Os Macabros” tem sido uma tarefa muito legal, apesar de ser apenas uma vez por ano. O bloco surgiu em 1998 e participa do carnaval da minha cidade natal (Kaloré) até hoje. Eu não fiz a primeira ilustração, mas fiz praticamente todas as outras.

Infelizmente, foi apenas nos últimos anos que passei a ilustrar usando softwares vetoriais. Antes usava apenas a velha combinação de lápis e papel, talvez por isso não tenha mais os desenhos originais, ou eles devem estar perdidos em alguma gaveta.

Fazer esse tipo de desenho é uma ótima oportunidade para exercitar a criatividade e tentar melhorar o traço, já que não tenho reservado tempo para outras ilustrações. Na sequência a seguir, os desenhos da frente e das costas das camisetas, de 2006 até 2009.

2006

Macabros_frente_2006Macabros_frente_2006

2007

Macabros_frente_2007macabros_costas_2007

2008

Macabros08_FrenteMacabros_Costas_2008

2009

macabros_frente_2009macabros_costas_2009

Ruby Gem: Escopo de busca fácil no ActiveRecord

Escrevi a algum tempo um post mostrando como usar campos FULL TEXT do MySQL para ter uma sistema de busca simples mas eficiente em suas aplicações Rails. Para quem não precisa de uma busca mais sofisticada, o plugin acts_as_fulltextable quebra o galho. 

Porém, recentemente me deparei com um situação onde precisava de algo ainda mais simples. Para um sistema de administração de conteúdo, a busca devia pesquisar apenas em um Model, e em apenas alguns campos. Portanto criar uma tabela extra no banco de dados, como o acts_as_fulltextable faz, seria um pouco dispendioso. 

Uma solução simples seria usar o método Model.find_by_name por exemplo, para buscar um registro pelo campo ‘name’, mas isso traria apenas um registro e sem a possibilidade de buscar por mais de um campo com uma única execução. Outra solução seria a criação de named_scopes, mas seria um tanto trabalhoso filtrar por vários atributos e ainda tratar a entrada de dados.

Foi então que encontrei a gem scoped_search. Quando instalada, ela adiciona um escopo para buscas em models do Active Record chamada search_for, a qual usa cláusulas LIKE da linguagem SQL para efetuar filtros, diretamente no banco de dados.

Instalação:

Adicione ao environment.rb de sua aplicação:


config.gem 'wvanbergen-scoped_search', 

           :lib => 'scoped_search', 

           :source => 'http://gems.github.com'

Depois, execute o comando:

$ rake gems:install

 

Utilização:

Basta adicionar uma linha no Model desejado, indicando os campos que serão usados na busca:


class User < ActiveRecord::Base

  searchable_on :login, :name

end

Depois da alteração, a classe passa a contar com o escopo search_for, que pode ser usado em cascata com outros named_scopes. No exemplo abaixo de uma action “search” do controller de usuários, integra-se a busca com a paginação provida pelo will_paginate:


 def search

   @users = User.search_for(params[:q]).paginate(:page => params[:page])

    respond_to do |format|

     format.html 

     format.xml  { render :x ml => @users }

   end

 end

Bem tranquilo. Para incrementar um pouco, podemos ainda adicionar campos de associações na busca. Supondo que o usuário tenha um perfil (profile), podemos adicionar o campo description pertencente ao perfil na busca, da seguinte maneira:


class User < ActiveRecord::Base

  has_one :profile, :dependent => :destroy

  searchable_on :login, :name, :profile_description

end

 

 
No MySQL, pode-se ainda utilizar índices FULLTEXT nos campos mais requisitados, para aumentar a performance das buscas.

FonteEasy search with ActiveRecord

Rails Summit: Emprego Novo!

Você pode estar pensando:

Mais um post sobre a Rails Summit? Não está um pouco atrasado não?

Atrasado, pode ser, mas foi porque estava esperando um bom momento para publicá-lo.

Foram 5 anos de desenvolvimento Delphi, onde eu tive o prazer de trabalhar com uma equipe muito competente e inovadora. Os desenvolvedores Delphi são normalmente taxados como “pregadores de botão”, por desenvolvedores de outras linguagens. Mas no caso da nossa equipe isso não acontecia. Com foco no desenvolvimento de ferramentas e frameworks, estávamos sempre buscando e aplicando práticas modernas de desenvolvimento e gerenciamento. E foi esse ambiente que me prendeu por esse tempo.

Mas agora chega a hora de novos desafios. Venho usando Ruby on Rails já há mais de 2 anos, apenas em projetos pessoais, desenvolvidos em meu tempo livre. A tecnologia me chamou tanto a atenção que passei a usá-la em aplicações, a escrever textos esporádicos aqui no blog, a participar da comunidade, me preparando para conseguir uma oportunidade de usá-la em tempo integral.

A oportunidade apareceu justamente durante a Rails Summit. Confesso que quando me inscrevi para o evento, eu sabia que essa seria uma ótima chance de conhecer de perto outros desenvolvedores e buscar oportunidades. Muito se falou sobre o evento, foram muitos posts, fotos e twitts destacando a qualidade das palestras e da organização, o que é realmente indiscutível e não pretendo ser repetitivo aqui. Mas além de tudo isso, para mim o evento teve essa outra face. Durante um dos intervalos entre as palestras, estava conversando com o pessoal da caravana paranaense e conhecemos Chad DePue, um americano residente em Buenos Aires que estava participando do evento e a procura de railers para trabalho remoto. Eu, logicamente, me candidatei =D.

Chad DePue na Rails Summit

Chad DePue na Rails Summit (foto por tapajos)

Depois do evento, tivemos algumas conversas e testes e agora posso dizer que sou o mais novo desenvolvedor da Reach Network, empresa sediada em Seattle, a qual o Chad presta serviços através da sua consultoria, a Inaka Networks.

Estou muito satisfeito em começar essa nova fase na minha carreira e gostaria de agradecer a toda a comunidade Rails que é muito receptiva e prestativa, especialmente aos Railers do paraná que conheci melhor durante o evento e ao Fábio Akita, por organizar um evento de tão alto nível como foi a Rails Summit. Além desses, uma figura importantíssima nesta mudança foi o Ozéias do RailsBox, que organizou muito bem a caravana paranaense e me deu várias dicas sobre trabalho remoto, valeu cara! 

Caravana paranaense (com alguns catarinenses também)

O evento superou as minhas expectativas, mas como sugestão para o próximo, espero que possamos ter avatares nos crachás e camisetas nerds para comprar =D

Valeu galera e até o próximo evento.

SEO on Rails: URLs Amigáveis

Deixar as URLs de sua aplicação amigáveis pode fazer uma grande diferença no ranking dos mecanismos de busca. Isso significa fazer com as URLs fiquem mais simples, criando endereços que sejam legíveis e que tragam alguma informação sobre o conteúdo da página.

Vejamos alguns exemplos de URLs complexas:

index.php?section=cine&category=comedy

ou

/cgi-bin/index.cgi?id=6F7YCH&page=6

Quais os problemas de ter URLs complexas?

  • A complexidade da URL torna o endereço difícil de memorizar
  • Em alguns casos fica difícil de saber o que você vai encontrar antes de clicar em um link desse tipo
  • A tecnologia utilizada pelo aplicativo fica exposta
  • Se a tecnologia utilizada no site tiver que ser alterada (php para ruby, por exemplo), todas as URLs antigas ficarão inválidas

A solução com Rails

Para quem usa Rails, principalmente da maneira Restful, já tem esses problemas minimizados. Basta fazer um scafold e as URLs para as actions já ficam em um formato simples, como no nosso exemplo de locadora abaixo:

Lista de filmes

http://locadora.com/filmes

Detalhes de um filme com id igual a 12

http://locadora.com/filmes/12

Novo filme

http://locadora.com/filmes/new

Edição de um filme com id igual a 12

http://locadora.com/filmes/12/edit

 

Esse é um dos conceitos de REST: a URL deve descrever o recurso disponibilizado. Acha que está bom? Está, mas podemos melhorar ainda mais. Não seria melhor se ao invés de identificadores numéricos, tivéssemos o próprio título do filme na URL? Por exemplo, a página com detalhes do filme teria a endereço:

http://locadora.com/filmes/de-volta-para-o-futuro

Existem algumas maneiras de fazer isso em um aplicação Rails. Uma bem simples é instalando o plugin permalink_fu:

$ script/plugin install git://github.com/cyu/permalink_fu 

Depois de instalado, basta fazer a seguinte chamada nosso Model Filme, para usar o campo título como identificador na URL:


  class Filme < ActiveRecord::Base

    has_permalink :titulo

  end

Logicamente o campo título terá seus caracteres especiais e espaços removidos para poder ser usado como endereço http.

Essa abordagem só é viável quando o título do filme é único, pois ficaria impossível distinguir entre dois filmes com o mesmo título, já que ambos teriam a mesma URL. Para estes casos, podemos sobrescrever o método to_params, usando o helper PermalinkFu.escape do plugin para remover acentos e espaços do campo que queremos usar na composição do endereço: 


class Filme < ActiveRecord::Base

  def to_param

    "#{id}-#{PermalinkFu.escape(titulo)}"

  end

end

Assim, teríamos o formato:

http://locadora.com/filmes/12-de-volta-para-o-futuro

Essa solução causa um pequeno efeito colateral, já que o parâmetro recebido pelo controller em params[:id] não terá apenas um valor do tipo inteiro, mas sim uma string. Observe o exemplo de método show do controller de filmes: 


class FilmesController < ApplicationController

  def show
   @filmes = Filme.find(params[:id].to_i)

   respond_to do |format|
     format.html # show.rhtml
     format.xml  { render :x ml => @filmes.to_xml }
   end

  end
end

Note que é necessário uma conversão do paramêtro ID para o tipo inteiro, em params[:id].to_i, para que o find funcione corretamente.

O uso de URLs amigáveis, além de deixar os endereços de sua aplicação mais fáceis de entender e de lembrar, também fazem com que o posicionamento nos buscadores melhore, já que palavras chave pesquisadas passam a aparecer também nos seus links. 

Leia mais sobre o uso de URLs amigáveis:

Criando URLs amigáveis em brunotorres.net

Better Search Engine Friendly URL’s with Ruby on Rails

SEO Optimization of URLs in Rails with to_param

SEO on Rails: Títulos e Meta-Tags

Otimizar um site para que ele seja bem posicionado pelos mecanismos de busca é uma preocupação recorrente a maioria os desenvolvedores web. As chamadas técnicas de SEO já não são segredo e existem muitos bons sites que ensinam como alcançar as primeiras páginas do Google. Logicamente apenas o próprio Google pode garantir uma posição em seu resultado, mas existem alguns fatores que quando levados em consideração influenciam diretamente esse posicionamento.

Quando criamos um sistema usando Rails, alguns pontos podem passar desapercebidos, devido as convenções usadas pelo framework. Pretendo iniciar uma série de posts mostrando como fazer SEO em aplicações Rails, mostrando alguns métodos e plugins que podem facilitar o nosso trabalho.

Técnicas de SEO

Para ter uma introdução sobre o que significa SEO e como são classificadas essas práticas, principalmente pelos mecanismos de busca, eu recomendo a leitura do artigo White, Gray e Black Hats. Neste post do tableless, Diego Eis mostra exemplos de técnicas White Hat, que são as recomendadas pelos buscadores, as técnicas Gray Hat, que se utilizadas se forma abusiva são consideradas anti-éticas, e ainda das Black Hat, essas sim tomadas como forma de enganar o ranking, e que quando descobertas são punidas com a remoção do site infrator.

As boas práticas recomendadas (White Hat) podem ser resumidas em: criar bom conteúdo e criar links internos. Para os buscadores, um bom conteúdo é um conteúdo relevante, ou seja, um conteúdo que por sua qualidade está sendo linkado por editores de outros sites. Além da qualidade, atualmente os buscadores verificam se esse conteúdo está formatado adequadamente, fazendo uso correto de meta-tags e estrturado de acordo com as definições da W3C. O Google, por exemplo, classifica melhor páginas bem formadas, com tags semanticamente corretas e uma boa hierarquia de títulos. Uma boa dica é sempre utilizar a ferramenta validação de HTML e CSS da W3C e ainda as ferramentas para webmasters disponibilizadas pelo próprio Google, que verificam diversos aspectos que alteram a indexação.

Títulos e Meta-Tags

Quem já teve um site indexado sabe da importância dada à tag com o título da página (<title>) e as chamadas meta-tags, com a descrição (<description>) e as palavras chaves (<keywords>), que vão no cabeçalho do HTML. Por isso, é importante que cada página da sua aplicação tenha conteúdos distintos nestas tags, algo que se encaixe com o conteúdo apresentado. Para facilitar esse trabalho, vou apresentar o plugin para Rails chamado meta-tags.

Se você tem o GIT instalado, vá até a pasta da sua aplicação e execute:

$ script/plugin install git://github.com/kpumuk/meta-tags.git

Feito isso, você já pode organizar melhor o conteúdo de suas meta-tags. Vejamos como utilizar o plugin em uma aplicação Rails, que para fins de exemplo, chamarei de “Locadora”.

No layout da aplicação (normalmente em app/views/layouts/application.html.erb), use o helper display_meta_tags, da seguinte maneira:


  <head>

    < %= display_meta_tags :site => 'Locadora' %>

  </head>

Este método é responsável por inserir as tags e title, description e keywords no HTML. Depois, podemos configurar valores como o título da página para cada view, usando a seguinte chamada:


  
<h1>< %= title 'Locações pendentes' %></h1>

Fazendo isso por exemplo, quando a página acima for mostrada, sua tag title será configurada de acordo com o conteúdo, no formato abaixo:


  <head>

    <title>Locadora | Locações pendentes</title>

  </head>

  <body>

    
<h1>Locações pendentes</h1>
  </body>
É possível ainda setar o valor das tags diretamente dos controllers, acessando variáveis de instância:

@page_title = 'Locações pendentes'
@page_description = 'Lista de locações que ainda não foram devolvidas.'
@page_keywords = 'Locadora, Locações, Pendentes'

Ou ainda, usando o método set_meta_tags:


set_meta_tags :title => 'Locações pendentes',
                     :description => 'Lista de locações que ainda não foram devolvidas.',
                     :keywords => 'Locadora, Locações, Pendentes'

Caso você queira, pode ainda usar opções extras para personalizar seus títulos:

:prefix – Para não mostrar o nome do site antes por exemplo, ou mostrar outro conteúdo

Ex.: <%= title ‘Locações pendentes’, :prefix => false %> #–> <title>Locações pendentes</title>

:separator – Texto usado para separar o nome do site do nome da página.

Ex.: <%= title ‘Locações pendentes’, :separator => “:” %> #–> <title>Locadora : Locações pendentes</title>

:reverse – Quando verdadeiro, inverte a posição no nome do site e o nome da página.

Ex.: <%= title ‘Locações pendentes’, :reverse => true %> #–> <title>Locações pendentes | Locadora</title>

Outras opções e formas de uso são melhor detalhadas no arquivo README.rdoc, que se encontra na pasta do plugin.

O plugin meta-tags facilita o gerenciamento do conteúdo de tags que são de suma importância para ter um website bem posicionado. Conteúdo esse que fica mais difícil de individualizar para cada view a medida que a aplicação cresce. Uma boa prática para o uso deste plugin é setar valores padrão das tags no controller da aplicação (application.rb) e depois customizar o conteúdo nas views.

Nos próximos posts desta série, teremos mais dicas de plugins para melhorar o SEO das suas aplicações Rails. Aguardem…

Leia mais sobre SEO:

Ruby Tips: Iterando em Arrays

Ruby sempre me atraiu pela facilidade com que posso usar as classes Array e Hash para manter estruturas de dados que em outras linguagens eu teria a necessidade de usar classes especiais, normalmente um classe para cada tipo de elemento, o que sempre deixava o código cheio de typecasts e muitos laços FOR ou WHILE para fazer iterações. Diferente de outras linguagens, em Ruby tudo é um objeto, inclusive um Array, que possui métodos que auxiliam na própria iteração com seus elementos. Todos esses métodos de iteração recebem um bloco de código que é executado para cada elemento do Array. Vou mostrar alguns exemplos de uso abaixo, então abra o IRB e vamos testar cada um deles:


numbers = [1, 2, 3, 4, 5]

Vou usar esse array como padrão para os exemplos.

Método Each

O método EACH recebe um bloco de código que será executado para cada elemento do Array. Cada elemento é passado para o bloco como parâmetro durante a iteração. Com isso podemos realizar operação com cada elemento, como por exemplo, imprimir uma frase com cada valor:


numbers.each do |number|
puts "Linha de numero #{number}"
end

Produzirá:
Linha de numero 1
Linha de numero 2
Linha de numero 3
Linha de numero 4
Linha de numero 5

Método Select

O método SELECT recebe um bloco de código que é executado para cada elemento. A diferença com o EACH é que enquanto este não retorna nenhum valor, o SELECT retorna um outro Array. Vamos comparar a utilização dos dois com um exemplo de seleção de números pares:


even = []
numbers.each do |number|
even < < number if number %2 == 0
end

Adicionamos ao Array even números com o resto da divisão por 2 igual a zero, obtendo [2,4]. Vejamos como o SELECT torna isso ainda mais fácil:


even = numbers.select do |number|
number % 2 ==0
end

Método Inject
O método INJECT também chama um bloco de código para cada iteração, mas com a diferença de passar 2 parâmetros a esse bloco. O primeiro deles é um acumulador e o segundo é o elemento. Vamos novamente comparar sua utilização com o EACH com um exemplo onde somamos todos os valores do Array numbers, primeiro usando o EACH e depois usando o INJECT:    


sum = 0
numbers.each do |number|
sum += number
end

sum = numbers.inject do |a, number|
a + number
end

O INJECT pode ainda receber um parâmetro, que é usado como valor inicial para o acumulador. Exemplo:


sum = numbers.inject(10) do |a, number|
a + number
end

Com o valor inicial sendo 10, sum passaria a ter o valor 25.

Método Map

O método MAP realiza a mesma iteração em um bloco de código como nos métodos anteriores, mas a resposta é um novo Array, com novos elementos baseado em operações aplicadas a cada elemento. Desta vez vamos comparar sua utilização com o EACH criando um Array com os valores de numbers multiplicados por 10. 


new_numbers = []
numbers.each do |number|
new_numbers < < number * 10
end

new_numbers = numbers.map do |number|
number * 10
end

Depois de qualquer uma das chamadas acima, new_numbers será [10,20,30,40,50]. Logicamente o uso do método MAP neste caso é mais limpo e elegante.

Espero que tenha esclarecido a diferença de uso dos iteradores de Array existentes em Ruby. Lembrando que esses mesmos métodos também existem em classes Hash. Tecnicamente a única diferença entre Arrays e Hashs é que na primeira classe os índices dos elementos são números inteiros, enquanto que um Hash por ter qualquer tipo de objeto como índice.

Fonte: RailsSpikes

Aplicações brasileiras em Rails e conferência Rails em outubro

Todo mundo que já pesquisou um pouco sobre Ruby ou Rails passou a conhecer Fábio Akita. Ele tem trabalhado bastante para tornar o desenvolvimento em Ruby On Rails cada vez mais popular aqui no Brasil. Para quem ainda não conhece (paraquedista com certeza =D), ele é um dos pioneiros a utilizar a tecnologia, escreveu o primeiro livro em português sobre o assunto e agora está à frente da equipe Rails da Locaweb, para oferecer uma hospedagem de qualidade seguindo o padrão dos hostings internacionais.

A fim de divulgar projetos de brasileiros feitos com Rails, ele publicou em seu blog uma lista de aplicativos que usam o framework e foram criados por brasileiros. Fico muito agradecido por ter um projeto citado entre outros tão importantes e que me serviram como inspiração. O Sismiko apareceu entre nomes como BlogBlogs, TreinaTom, Pagestacker, Mapia, Investidor Virtual e Ikwa, só para citar alguns.

Espero que o Sismiko também inspire desenvolvedores recém chegados ao Rails à transformar suas idéias em realidade. Não deixe de conferir a lista completa de aplicativos e até sugerir algum que você conheça mas tenha ficado de fora.

Se meus planos se confirmarem, poderei encontrar os desenvolvedores desses aplicativos na Rails Summit Latin América que acontece em Outubro, no Anhembi em São Paulo. Se você estava dando uma voltinha por Marte nos últimos dias =D, não deve saber que esta será, com certeza, a maior conferência sobre Rails realizada abaixo da linha do Equador. Nas palavras do próprio Akita:

Teremos alguns dos maiores Railers da comunidade internacional como David Hansson, Charles Nutter, Chad Fowler, Dr. Nic, Chris Wanstrath, David Chelinsky e muito mais, em dois dias inteiros, com duas sessões paralelas, incluindo almoço, coffee break por R$ 300 (cerca de USD 190).

Além dessas atrações internacionais, teremos palestras de brasileiros empreendedores Rails: Manoel Lemos do Brasigo e BlogBlogs, Carlos Eduardo da e-Genial e Treina Tom, Fabio Kung da Caelum, Vinicius Teles da Improve it (nosso famoso evangelista de XP) e George Guimarães do Pagestacker.

O site oficial do evento será lançado em breve, com venda de ingressos e dicas de transporte e hospedagem.

BDD com Shoulda: Menos código, testes mais legíveis

Para quem ainda não conhece, BDD (Behavior Driven Development) é uma prática de processos ágeis que vem mudando a maneira como os desenvolvedores escrevem testes automatizados para seus sistemas.  Testes escritos sobre este conceito são mais fáceis de entender e podem ser utilizados como ferramenta de modelagem de um sistema, já que eles ajudam pessoas com pouco conhecimento técnico à entender funcionamento do software. Além disso, testes simples de entender também são simples de manter.

Essas vantagens me fizeram pensar na idéia de migrar os testes do meu projeto Rails, que tinha seus testes unitários escritos sobre o framework Test/Unit, o padrão utilizados pelo Rails para TDD (Test Driven Development). Para realizar a mudança era necessário aprender a utilizar outro framework, no caso o RSpec.

Foi então que, acompanhando a blogosfera de Railers, através de um post do Carlos Brando, conheci o Shoulda. O Shoulda é um framework recente e que eu considero como um passo intermediário entre o Test/Unit e o RSpec. Ele traz um conjunto de classes e métodos que melhoram o Test/Unit padrão, permitindo o uso de uma sintaxe parecida com a do RSpec. Assim é possível se aproximar de BDD sem ter muito impacto na forma de programar os testes.

Vou exemplificar aqui o migração dos testes de um model simples do Sismiko, a classe City que possui apenas os atributos “name” e “state_id”, representando o nome da cidade e o id do estado, respectivamente.

Observe o código utilizando o modo padrão:


class CityTest < Test::Unit::TestCase

fixtures :cities

  def test_name_required #testa se o nome da cidade é um campo requerido

    check_required(:name)

end

def test_state_required #testa se o campo state_id da cidade é um campo requerido

check_required(:state_id)

end

def test_max_name #testa se o tamanho máximo do campo nome é de 100 caracteres

city = create(:name => "a" * 101)

assert city.errors.invalid?(:name),"name must have a maximum of 100 chars"

assert_not_nil city.errors.on(:name)

assert !city.valid?, "city shouldn't be created"

end

private

def create(options={}) #cria uma cidade padrão

City.create({

:name => "Maringá",

:state_id => 1

}.merge(options))

end

def check_required(att) #verifica se o campo passado em att é requerido pelo objeto

city = create(att => nil)

assert city.errors.invalid?(att)

assert_not_nil city.errors.on(att)

assert !city.valid?, "city shouldn't be created"

end

end

No código acima vemos três testes sendo feitos e mais dois métodos auxiliares, para criar um objeto com valores padrão em seus atributos e outro para verificar se um atributo é requerido. Quanto as testes, o primeiro e o segundo testes verificam se os campos “name” e “state_id” são obrigatórios ao criar um objeto de cidade. O terceiro verifica se o campo “name” aceita no máximo 100 caracteres.

Vejamos agora o código necessário para fazer os mesmo testes, usando a declaração de contextos pregada pelo BDD para deixar os testes mais legíveis, já utilizando o Shoulda:


class CityTest < Test::Unit::TestCase

fixtures :cities

context "A City instance" do

setup do

@city = create

end

should "have a required name" do

check_required(:name)

end

should "have a required state" do

check_required(:state_id)

end

context "with a large name" do #uso de contexto aninhado

setup do

@city.name = "a" * 101

@city.save

end

should "validate max size of name" do

assert city.errors.invalid?(:name)

end

end

end

private

def create(options={}) #cria uma cidade padrão

City.create({

:name => "Maringá",

:state_id => 1

}.merge(options))

end

def check_required(att) #verifica se o campo passado em att é requerido pelo objeto

city = create(att => nil)

assert city.errors.invalid?(att)

assert_not_nil city.errors.on(att)

assert !city.valid?, "city shouldn't be created"

end

end

Com certeza ficou mais fácil de entender e também de localizar o erro. Por exemplo, caso o teste de atributo “name” requerido falhe, recebemos a seguinte mensagem:

test: A City instance should have a required name

O uso do novo framework melhorou a legibilidade dos testes, mas podemos melhorar ainda mais. Fazendo uso de helpers acrescentados pelo Shoulda, é possível reduzir o código para algo assim:

 
CityTest < Test::Unit::TestCase

  fixtures :cities

  should_require_attributes :name, :state_id #1

  should_ensure_length_in_range :name, (0...100) #2

end

Sem dúvida, muito mais simples. A linha comentada com #1 substitui dois testes escritos anteriormente, validando os campos “name” e “state_id” como requeridos. Em #2 verifica-se se o atributo “name” aceita valores com tamanho entre 0 e 100, rejeitando valores maiores.

Ficou impressionado? Instale o Shoulda e faça suas experiências, o projeto está no github:

script/plugin install git://github.com/thoughtbot/shoulda.git

Veja também:

O Lucas Hungaro fez um screencast muito bacana com uma introdução ao Shoulda.

Existem vários outros helpers além dos apresentados aqui. A documentação de todos eles pode ser encontrada na documentação do projeto.

Movendo o YellowPages.com para Rails: Um caso de sucesso

Diante de tanta polêmica sobre o possível problema de escalabilidade do Rails, que seria a causa da instabilidade do Twitter, na RailsConf 2008 aconteceram várias apresentações tentando desmistificar o fato, mostrando maneiras de projetar aplicações que suportem milhares ou até milhões de requisições por dia.

Nada melhor que demonstrar isso e mais uma série de vantagens do Rails com um caso de uso real. Foi o que fez John Straw, arquiteto de software chefe da YellowPages.com, com a apresentação “Sobrevivendo a uma grande reescrita – Movendo YellowPages.com para Rails“. A minha intenção com este post é fazer uma tradução livre e resumida de seus tópicos.Páginas amarelas

Para quem não conhece, a YellowPages.com é o site das “páginas amarelas” dos Estados Unidos. O equivalente a Telelistas.net só que, acredito eu, com números mais expressivos: 23 milhões de visitantes por mês, 2 milhões de buscas por dia, mais de 48 milhões de requisições por dia, sendo mais de 1500 por segundo.

A chamada “Grande reescrita” começou no final de 2006. O sistema existente era escrito em Java, com 125.000 linhas de código, e mantido por consultores externos. Em apenas três meses de desenvolvimento, uma equipe de 20 pessoas (sendo apenas 5 desenvolvedores) portou o sistema para Rails, resultando em apenas 20.000 linhas código. O que impressiona é que a equipe havia estimado o prazo de 1 ano para a tarefa.

Mas por que decidiram reescrever o sistema?

O sistema do YellowPages já estava sem mudanças de design desde 2003 e, de acordo com testes de usabilidade, a experiência que o usuário tinha ao navegar não era satisfatória.

Além do design, a plataforma era difícil de manter, de aplicar técnicas de SEO e a implementação de sessões tornava o sistema difícil de escalar horizontalmente. O código estava cheio de remendos e não havia testes, fazendo da tarefa de adicionar novas funcionalidades algo muito trabalhoso.

Para resolver esses problemas e ainda criar uma arquitetura orientada a serviços, decidiram pelo RubyOnRails, usado em 90% da nova estrutura.

O que os levou a escolher Rails?

Um dos requisitos principais para a nova aplicação era proporcionar controle sobre a estrutura de URLs, principalmente para uso de SEO. Avaliando frameworks Java, nenhum apresentou um controle satisfatório. Logo, a escolha ficou entre Rails e Django.

Rails acabou levando a melhor sobre Django por que:

  • Possuía uma melhor integração com testes automatizados;
  • Era uma plataforma mais madura;
  • Existia um caminho claro para C, caso necessitassem de mais performance;
  • Os desenvolvedores se sentiam mais confortáveis e experientes.

Como ficou a arquitetura da aplicação?

Arquitetura da aplicação

Arquitetura da aplicação (clique para ampliar)

A figura acima mostra como ficou o design da aplicação, dividida em três camadas: web, serviço e busca, cada uma delas separada por um hardware de balanceamento de carga F5. O Apache também foi usado no gerenciamento dos processos mongrel, espalhados pela camada web e camada de serviço. Vejamos outras características da arquitetura:

  • Comunicação HTTP em todos os níveis sem salvamento de estado (Stateless)
  • A camada de serviço responde a REST e no formato JSON
  • Memcached foi usado extensivamente na camada de serviço
  • Ambiente de produção: 25 servidores para a aplicação + 2 servidores para banco de dados Oracle em cada data center.

Conseguiram a performance desejada?

Algumas metas de performance foram estabelecidas: A média de carregamento da página inicial deveria estar abaixo de 1 segundo, para páginas de busca a resposta deveria estar em menos de 4 segundos e suportar todo o tráfego sem quedas. Para isso, foram necessárias otimizações:

  • Consideraram o uso de balanceamento de carga por hardware: F5 vs. HAProxy vs. Swiftiply vs. Localhost;
  • Utilizaram mongrel_handlers para direcionar requisições da camada web para a de serviços sem passar pelo Rails;
  • Desenvolveram uma biblioteca em C para organizar as respostas do cluster de buscas;
  • Utilizaram Erubis para acelerar a renderização das views da camada web;
  • Minimizaram o tamanho do JavaScript e mudaram de Prototype para jQuery.

A reescrita foi considerada um sucesso!

Depois de todo o trabalho, o novo sistema apresentou melhor desempenho que o sistema antigo em Java, com a vantagem de ser feito em Rails e dirigido por testes, o que o torna fácil de manter e de estender.

Para concluir a apresentação, John Straw destacou alguns itens considerados chaves para o sucesso do projeto:

  • Equipe de desenvolvimento pequena e talentosa;
  • Avaliação cuidadosa da tecnologia e escolha da plataforma compatível com a aplicação;
  • Fácil comunicação entre os membros da equipe com diversos pontos de vista permitiu uma captura de requisitos sem formalismo;
  • A regra “Não altere coisas que não são simples de decidir” preveniu a tomada de decisões que paralisavam o projeto;
  • A criação de um site “beta” constantemente atualizado possibilitou uma visualização clara de progresso e direcionamento.

Veja mais detalhes sobre todo o processo de reescrita no PFD da apresentação de John Straw, disponível em http://en.oreilly.com/rails2008/public/asset/attachment/2765.

Fontes:  RailsOnWave e BuildingWebApps.

Próxima Página »