O Active Record é um dos principais componentes do Rails, pois é exatamente o model do MVC. Ele mapeia automaticamente uma classe numa tabela do banco de dados, criando métodos para acesso a cada atributo. Porém, e se quisermos desenvolver uma aplicação sem banco de dados?

Passei por esta situação recentemente: a aplicação acessa um servidor diretamente, via Atom, e, portanto, não precisa armazenar dados localmente. Porém, não basta comentar todas as linhas do arquivo config/database.yml, pois o Rails mostrará uma mensagem de erro informando que não encontrou o banco de dados correspondente.

Para resolver este problema, resolvi, inicialmente, tentar não utilizar o Active Record. Para isso, primeiramente precisei modificar o model que eu tinha na aplicação, retirando a herança de ActiveRecord::Base. Porém, ao tentar executar o servidor, recebi mensagens de erro informando que o banco de dados não foi encontrado. Isso ocorre porque, apesar de não haver qualquer classe herdando de ActiveRecord::Base no projeto, este módulo estava sendo carregado. Consequentemente, o Rails tentava ler o arquivo config/database.yml, que não estava configurado, resultando na mensagem de erro. Para evitar este problema, foi necessário evitar o carregamento do Active Record explicitamente, acrescentando a linha abaixo ao arquivo config/environment.rb:

config.frameworks -= [ :active_record ]

Feito isto, o Rails passa a funcionar sem banco de dados e sem Active Record. Porém, surgiu um outro problema: os testes unitários do Rails pararam de funcionar, pois a classe ActiveSupport::TestCase, que é a classe base para os testes unitários, não funciona sem Active Record. Para resolver, troquei a herança desta classe para Test::Unit::TestCase, que era utilizada como padrão antes da versão 2 do Rails. Não sei exatamente quais são as diferenças entre as duas, mas só consegui resolver este problema desta forma - se alguém descobrir alguma outra solução, me avise!

Também precisei carregar explicitamente o model no arquivo de teste, pois o Rails deixou de fazer a referência automática a esta classe. Feitas as alterações, o arquivo de teste (ex: test/unit/usuario_test.rb) ficou assim:

require 'test/test_helper'
require 'test/unit'
require 'app/models/usuario'

class UsuarioTest < Test::Unit::TestCase
  # Replace this with your real tests.
  def test_truth
    assert true
  end
end

O uso de IDEs para desenvolvimento em Rails é um assunto bastante controverso. Devido às diversas facilidades no desenvolvimento com este framework, muitas pessoas afirmam que uma IDE não é necessária, basta um editor de textos simples e um terminal. Outros acreditam que, apesar do alto grau de automação do Rails, ainda é vantajoso usar uma IDE. No meio desta discussão, acredito que, no caso do Ruby on Rails, a escolha torna-se um gosto pessoal.

Para os que estão no primeiro caso, a escolha do editor de texto também é uma questão de gosto pessoal. Alguns se sentem confortáveis com o vi, outros com o Emacs. Estes casos são mais comuns quando já há uma intimidade com estes editores - dificilmente alguém vai aprender a usar Emacs para desenvolver em Rails. Para quem prefere um editor mais amigável, há opções para todos os sistemas operacionais: no Linux, o Gedit, quando bem configurado com alguns dos diversos plugins disponíveis, torna-se uma poderosa ferramenta de desenvolvimento, com code completion, por exemplo. No Mac há o TextMate e no Windows o SciTe (também disponível para Linux). Para tarefas como rodar o servidor web, executar migrations e generates, debugar a aplicação ou trabalhar com uma ferramenta de controle de versão (CVS, SVN, GIT, etc), usa-se o terminal.

Para quem prefere usar uma IDE, a principal vantagem é não precisar recorrer ao terminal para executar as tarefas acima. Tudo é integrado, facilitando o trabalho. Realmente algumas dessas tarefas são extremamente simples, como executar migrations. Nestes casos, pouca diferença há entre usar a IDE ou o terminal. Porém, trabalhar com uma ferramenta de controle de versão e executar um debug por linha de comando pode ser trabalhoso. Acredito que estas sejam as principais vantagens de se usar uma IDE. Por outro lado, as IDEs requerem bastante memória do desktop, ao contrário dos editores de texto, e costumam ser mais instáveis.

As opções de IDE para Rails também são variadas, e as duas principais são o Aptana RadRails e o NetBeans. Nos últimos meses trabalhei com ambos alternadamente, e encontrei diversas vantagens e desvantagens em cada um.

Aptana RadRails

O Aptana RadRails pode ser instalado como um plugin do Eclipse ou isoladamente, caso você não o tenha instalado. Na primeira vez que o testei, há alguns meses, achei o plugin bastante instável, e com alguns bugs incômodos. Recentemente, ao verificar que havia uma nova versão disponível, testei novamente, e verifiquei que esta nova versão está bem melhor. Há alguns meses atrás, escrevi neste post que o RadRails não conseguia ler arquivos database.yml usando o formato descrito. Conforme este comentário do Chris Williams, um dos desenvolvedores do RadRails, a nova versão corrigiu este problema e outros que eu havia encontrado. A versão mais recente também possui um Ruby Shell bastante útil, inclusive com autocomplete de comandos e parâmetros, porém instável - já o vi travar algumas vezes. Outras vantagens do RadRails são:

  • Botões para acesso rápido ao Model, View, Controller, Helper e Test equivalentes ao arquivo ativo
  • Problema do DRY no database.yml corrigido
  • Suporte a testes mais completo (permite executar apenas um arquivo de testes de cada vez)

As principais desvantagens do RadRails são:

  • Problemas no autocomplete (não mostra todos os métodos)
  • O console não aceita a tecla para cima para acessar os últimos comandos, como no shell
  • Ruby shell instável (trava com frequência)
  • Server não permite selecionar um environment diferente dos 3 defaults (development, test e production), mesmo que você tenha algum outro environment definido no arquivo database.yml

NetBeans

O suporte do NetBeans ao Rails tornou-se estável há mais tempo que o RadRails, com um editor para código Ruby bem completo. Atualmente, ambos diferenciam-se nos detalhes. Principais vantagens do NetBeans:

  • Ao selecionar um texto e digitar (, o editor envolve o texto selecionado com parênteses
  • Ao selecionar um texto e digitar #, o editor envolve o texto selecionado com #{ e }
  • Ao colocar o cursor sobre um end, o editor destaca o inicio desse bloco/método/classe
  • O find é mais fácil de usar (estilo Firefox, com highlight automático dos termos conforme você digita)

Principais desvantagens do NetBeans:

  • Problemas no autocomplete (mostra os métodos que não deveriam aparecer)
  • O console não aceita a tecla para cima para acessar os últimos comandos, como no shell
  • Suporte a SVN bastante limitado
  • A execução do server não permite selecionar o environment (sempre utiliza o development)
  • A opção Test executa todos os testes (não há opção para executar apenas um arquivo, ou apenas os testes unitários, por exemplo)

Concluindo, o editor do NetBeans me parece mais completo para tratamento de código Ruby. Porém, o ambiente do RadRails é mais completo para execução de testes, integração com SVN e outras tarefas. Um ponto onde ambos apresentam problemas é o autocomplete - às vezes aparecem métodos de mais, outras vezes de menos. Porém, devemos reconhecer que, por se tratar de uma linguagem dinâmica, onde uma variável não tem tipo fixo, é bastante complicado termos um autocomplete realmente preciso.


Muito se fala das vantagens do Ruby sobre muitas das linguagens atuais, por ser uma linguagem de altíssimo nível. Mas muitas vezes não percebemos grandes diferenças entre as linguagens, além das usuais diferenças de sintaxe - se as linhas de comando precisam de ponto-e-vírgula no final, se as variáveis precisam de $ no início do nome, se estas são tipadas ou não, se precisam ser declaradas ou não, etc. Porém, em algumas situações específicas, enxergamos o verdadeiro poder do Ruby.

Hoje precisei implementar uma paginação de resultados de busca. Eu sei que existem plugins para Rails que simplificam esta tarefa, porém como esta busca não é feita no banco de dados, e sim através de um indexador que já retorna resultados paginados, optamos por fazer manualmente a lista de links para as páginas.

Por exemplo: ao realizar uma busca, são exibidos os 10 primeiros resultados, com um link para a próxima página e a lista de links para cada página. Como o número de páginas, teoricamente, não tem limite, fiz o seguinte:

  • caso o resultado tenha até 10 páginas, todas são exibidas
  • caso o resultado tenha mais de 10 páginas, são exibidas apenas 10, sendo que:
    • caso a página atual seja uma das 6 primeiras, exibe os links para as páginas 1 a 10
    • caso a página atual seja maior que 6, exibe da página atual - 5 até a página atual + 4
    • caso a página atual seja uma das 10 últimas, exibe as 10 últimas

Pela descrição acima, percebemos que é uma lógica bem simples, porém meio chata para ser implementada - na maioria das linguagens atuais, isto exigiria um grande número de if’s aninhados, para verificarmos se as condições descritas acima são atendidas. Porém, em Ruby o código ficou muito simples e enxuto:

if (num_pages > 1)
  page_start = [1, page-5].max
  page_end = [num_pages, page+4].min
  if num_pages > 10
    page_start = [page_start, num_pages-9].min
    page_end = [page_end, 10].max
  end
  page_start.upto(page_end) {|p|
    # Exibe os links
  }
end

Recentemente foi lançado o Phusion Passenger, mais conhecido como mod_rails. Trata-se de um módulo Apache para Rails que promete oferecer configuração e deploy de aplicações mais simplificado do que com o Mongrel, além de ser mais estável e utilizar menos memória.

Tentei instalar o mod_rails e encontrei várias dificuldades. Inicialmente tentei instalar no CentOS 4.4, mas não consegui. Encontrei alguns conflitos de versões de pacotes (pré-requisitos do mod_rails), e ainda não consegui concluir a instalação.

Em seguida, tentei instalar no Ubuntu 7.10. As dificuldades foram menores, mas ainda assim não foi tão simples quanto parece pelo guia do usuário. Segue o passo-a-passo da instalação:

  • Instalar os pré-requisitos:

      sudo apt-get install apache2-mpm-prefork apache2-prefork-dev libapr1-dev
    
  • Instalar a gem do mod_rails:

      sudo gem install passenger
    
  • Definir as seguintes variáveis de ambiente:

      export HTTPD=/path/to/httpd
      export APXS=/path/to/apxs (ou apxs2)
    
  • Executar o script de instalação do módulo Apache:

      sudo /usr/lib/ruby/gems/1.8/gems/passenger-1.0.1/bin/passenger-install-apache2-module
    
  • Habilitar o mod_rails no arquivo httpd.conf do Apache, adicionando as linhas a seguir:

      LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-1.0.1/ext/apache2/mod_passenger.so
      RailsSpawnServer /usr/lib/ruby/gems/1.8/gems/passenger-1.0.1/bin/passenger-spawn-server
      RailsRuby /usr/bin/ruby1.8
      RailsEnv PROD
    

A última linha acima define o ambiente Rails que será utilizado. Se você omitir esta linha, será usado o ambiente padrão (production).

  • Criar um virtual host no Apache:

      <virtualHost *:80>
        ServerName localhost
        DocumentRoot /var/www/rails/public
      </virtualHost>
    

Na configuração acima, DocumentRoot é o diretório public da sua aplicação Rails.

Ao concluir estas configurações e reiniciar o Apache, minha aplicação funcionou, porém os arquivos que estão no diretório public (arquivos javascript, CSS e imagens) não estavam acessíveis. Para resolver este problema:

  • habilite o mod_rewrite
  • adicione à configuração do virtual host:

      <directory "/var/www/rails/public">
          Options         FollowSymLinks
          AllowOverride   All
      </directory>
    
  • reinicie o Apache

Assim, finalmente consegui fazer a aplicação funcionar corretamente. Ainda não fiz nenhum benchmark comparando o mod_rails com o Mongrel, mas todos os que encontrei até agora são favoráveis ao mod_rails, como estes:


A classe ActiveRecord::ConnectionAdapters::Column tem um método human_name que cria uma versão “humanizada” para os nomes das colunas de tabelas (atributos de um model). Porém, nem sempre o nome criado é o que desejamos. Por exemplo, se temos uma coluna num_usuarios, o método human_name retornará “Num Usuarios”, que, provavelmente, não é o que queremos. Para configurar o human_name manualmente, há duas soluções:

  1. criar um hash e redefinir o método human_attribute_name:
class Model < ActiveRecord::Base
  HUMANIZED_ATTRIBUTES = {
    num_usuarios => 'Número de usuários'
  }

  def self.human_attribute_name(attr)
    HUMANIZED_ATTRIBUTES[attr.to_sym] || super
  end
end
  1. usar o plugin human_attribute_override. Esta solução é mais simples e elegante:
class Model < ActiveRecord::Base
  attr_human_name :num_usuarios => 'Número de usuários'
end