O Ruby on Rails é um framework escrito em ruby por David Heinemeier Hansson. Ao implementá-lo, duas filosofias importantes foram favorecidas:

  • Don't Repeat Yourself – DRY: Duplicar código nunca é a melhor solução, porque isso pode indicar falta de conhecimento, então um dos conceitos que o Rails favorece é a não repetição de códigos ou ideias;
  • Convention Over Configuration (Convenção sobre configuração): Ao invés de termos muitos arquivos .properties ou .xml para configurar padrões de URL, parâmetros e outros, podemos usar convenções.

Vamos criar uma aplicação de agenda de contatos usando os geradores do Rails e, em seguida, vamos varrer o código para analisar e entender o funcionamento do que foi criado com base nessas duas ideias.

MVC

Os aplicativos feitos em Rails seguem o padrão da Arquitetura MVC, que separa os componentes em Modelos (M), Visões (V) e Controles (C), como apresentado na Figura 1.

Arquitetura Model, View,
Controller

Figura 1. Arquitetura Model, View, Controller

  • Modelos: Os modelos são responsáveis, basicamente, por representar os nossos dados, além de conter as regras de negócio da aplicação ou sua lógica;
  • Visões: As visões são responsáveis pela apresentação dos dados da aplicação ao usuário, por exemplo, uma tabela que lista os contatos de uma agenda;
  • Controles: Os controles recebem uma requisição e fazem a mediação entre qual regra vai ser executada, ou que página vai ser renderizada, etc.

Instalação

Para iniciarmos o desenvolvimento será necessário ter o Rails instalado na sua máquina. Se estiver usando um ambiente Debian ou Ubuntu, digite o seguinte comando no seu terminal:

   sudo apt-get install ruby-full

Em ambientes como o Fedora ou CentOS, abra o terminal e digite o seguinte:

   sudo yum install Ruby
  gem install rails 

Caso esteja utilizando Windows (embora alguns autores não aconselhe o uso deste SO para desenvolver com Rails), poderá pesquisar por RubyInstaller e RailsInstaller.

Nota: Qualquer dúvida com relação a instalação e possíveis erros ou problemas com versões consulte a documentação do Rails (seção Links) ou o tutorial de instalação do Ruby aqui no portal.

Ambiente de desenvolvimento e a aplicação

Para este artigo trabalharemos usando o ambiente Ubuntu, então, os comandos de criação de pasta/arquivo, listagem de conteúdo de diretórios, entre outros, não irão rodar no prompt ou cmd.

Para iniciarmos abra o seu terminal/cmd, e entre em um diretório onde você gostaria de guardar os fontes da sua aplicação. Para esse exemplo utilizaremos a /home/daniel/RailsProjects. Em seguida digite o código:

rails new agenda

Na saída do terminal vemos logo uma grande quantidade de diretórios e arquivos criados pelo Rails, como mostra a Figura 2.

Saída do comando rails new

Figura 2. Saída do comando rails new

A maior parte do código que mais nos interessa. está no diretório app, que é subdividido em algumas outras pastas, como models, views, controllers, que armazenam (como indicado pelo nome) as classes de modelos, os arquivos de visão e as classes que representam os nossos controladores, respectivamente.

O framework também criou o arquivo GemFile, que registra todas as dependências do nosso projeto. Na pasta config estão alguns poucos arquivos que dizem respeito a configuração, dentre os quais temos o database.yml e o routers.rb.

O arquivo database.yml guarda os dados para acesso ao banco de dados. Por isso, se você quiser configurar sua aplicação para acessar outro banco de dados (Postgres, MySQL) ao invés do SQLite (que é o banco de dados padrão), basta alterá-lo. Já o arquivo routers.rb armazena as rotas para as ações dos nossos controllers.

Scaffold

Com a estrutura da nossa aplicação montada, agora vamos para a primeira fase do projeto, que é a criação de um CRUD de contatos. Para isso vamos usar o Scaffold, que vai gerar absolutamente tudo que precisamos para isso. No terminal, dentro do diretório do projeto “agenda”, digite a seguinte linha de comando:

rails generate scaffold contato nome:string email:string celular:string

A saída gerada é apresentada na Figura 3.

Saída do comando rails generate scaffold

Figura 3. Saída do comando rails generate scaffold

Esse comando gera para o desenvolvedor o CRUD completo apenas passando a nossa entidade (neste caso contato) e seus atributos seguidos do tipo, como nome:string email:string celular:string. Existem outros tipos que podemos usar comoboolean, date, decimal, float, integer, text entre outros.

Repare que com esse comando o Rails mais uma vez criou uma série de arquivos.

Para que possamos fazer alguns testes nas nossas classes, vamos gerar a nossa base de dados e criar a tabela contatos. Para isso digite no seu terminal o seguinte comando:

rake db:create

Este criará dois bancos de dados: o agenda_development (o padrão, visto que estamos desenvolvendo) e agenda_test.

Agora digite o seguinte comando para que o Rails crie a tabela 'contatos':

rake db:migrate 

O rake é uma gem para gerenciamento de tarefas, semelhante ao make do Unix. É comumente usado para tarefas de administração que podem ser executadas com scripts. Também é possível criar suas próprias tarefas para serem executadas com rake.

A seguir temos algumas tarefas predefinidas, mas existem outras:

  • rake about -lista versão do rails e das dependências associadas ao projeto;
  • rake db:create- cria o banco de dados usando as propriedades descritas em config/database.yml;
  • rake routes - lista todas as rotas definidas;
  • rake test- executa todos os testes funcionais, unitários e de integração.

Caso queira ver o resultado do que fizemos até aqui funcionando basta executar o seguinte comando no terminal:

rails server

Assim iniciaremos o servidor e a nossa aplicação estará disponível em http://localhost:3000/contatos. Neste link você notará que já temos as operações do CRUD disponíveis para a entidade Contato, como mostram as Figuras 4 a 6.

Apresentação da página de listagem de contatos

Figura 4. Apresentação da página de listagem de contatos

Apresentação da página de cadastro de contatos

Figura 5. Apresentação da página de cadastro de contatos

Apresentação da página de edição de contatos

Figura 6. Apresentação da página de edição de contatos

Modelo

Vamos moldar o nosso CRUD com a arquitetura MVC. Comece abrindo a classe 'Contato' gerada, localizada em /agenda/app/models/contato.rb. A mesma deve estar parecida com o que se segue:

lass Contato < ActiveRecord::Base
  End

A nossa classe 'Contato' representará a nossa tabela contatos. Ela estende (<) a classe 'ActiveRecord::Base' do Rails, que mapeia ou implementa de forma transparente ao desenvolvedor o acesso ao banco de dados.

Por estender essa classe já temos as funcionalidades apresentadas a seguir. Para cada uma mostramos o código SQL equivalente:

  • Procura o contato cuja chave primária seja 1:
    contato = Contato.find(1)
    SELECT * FROM contatos WHERE contatos.id = 10 LIMIT 1
  • Recupera o primeiro contato ordenado pela chave primária:
    contato = Contato.first
    SELECT * FROM contatos ORDER BY contatos.id ASC LIMIT 1
  • Recupera o último contato ordenado pela chave primária:
    contato = Contato.last
    SELECT * FROM contatos ORDER BY contatos.id DESC LIMIT 1
  • Retorna todos os registros da tabela contatos:
    contatos = Contato.all
    SELECT * FROM contatos
  • Retorna contatos baseados em um determinado campo
    contatos = Contato.find_by nome: 'daniel'
    SELECT * FROM contatos WHERE contatos.nome = 'daniel'
  • Retorna contatos baseado em condições:
    contatos = Contato.where(“nome LIKE ?”, “%daniel%”)
    SELECT * FROM contatos WHERE contatos.nome LIKE '%daniel%'

Muitos outros métodos estão disponíveis só pelo fato de estendermos ActiveRecord::Base, poupando-nos, inclusive, de implementar as consultas SQL específicas para cada banco de dados.

Além de todos esses métodos citados, essa classe também disponibiliza os métodos de leitura e escrita para cada propriedade que a tabela possui:

  • Método de escrita da propriedade 'nome' de um contato:
    contato.nome = 'joão da silva'
  • Método de leitura da propriedade 'nome' de um contato (neste caso será impresso no console 'joão da silva'):
    puts contato.nome 

Controlador

Vejamos agora o que o Rails fez na classe ContatosController, que está localizada em agenda/app/controllers/contatos_controller.rb.

Depois da declaração da classe (class ContatosController < ApplicationController), o arquivo inicia com a seguinte instrução:

before_action :set_contato, only: [:show, :edit, :update, :destroy]

Isso quer dizer que antes da execução das actions show, edit, update e destroy, o método set_contato será executado. E isso é necessário visto que todas essas actions listadas recebem na requisição um parâmetro id, que é a chave primária do objeto. O que o método faz é simplesmente recuperar o objeto que tem o identificador passado como parâmetro, como vemos a seguir:

def set_contato
    @contato = Contato.find(params[:id])
  end

Esse método evita a repetição dessa lógica nos demaisque precisam dela. Se não o tivéssemos seria necessário repetir a linha @contato = Contato.find(params[:id]) nos métodos listados em before_action.

A seguir temos a definição do método index:

def index
    @contatos = Contato.all
  end

Ele busca todos os registros pelo método all da classe ActiveRecord::Base e armazena na variável de instância @contatos. Ao acessar esse método, o Rails automaticamente redireciona para a página index.html.erb. As variáveis de instância ficam visíveis na camada de visão, então é possível percorrer essa lista e construir, por exemplo, uma tabela, como veremos a frente.

O próximo método é o show, que é responsável por buscar o usuário passado no parâmetro id e disponibilizado na camada de apresentação para que seja construída a página show.html.erb:

def show
  end

Mas como ele faz isso tudo se está vazio? Lembre-se que o método set_contato está sendo chamado antes (before_action) do método show.

Os métodos new e edit funcionam de forma semelhante, pois eles “redirecionam”, respectivamente, para as páginas new.html.erb e edit.html.erb. Não se esqueça que antes da execução de ambos, o método set_contato também será chamado para recuperar o objeto baseado no identificador passado na requisição.

A seguir temos o método create, presente na Listagem 1.

Listagem 1. Método create


def create
@contato = Contato.new(contato_params)
   
  if @contato.save
   redirect_to action: 'show', id: @contao.id notice: 'Contato was successfully created.'
  else
   render action: 'new' 
  end
end

A primeira linha desse método cria um novo contato baseado nos parâmetros recebidos por contato_params, escolhendo quais atributos serão atribuídos diretamente pelo usuário, o que é conhecido como mass assignment (atribuição em massa). Sem essa característica teríamos que escrever código para recuperar o valor passado pelo usuário para cada campo e atribuí-lo no modelo, como nos exemplos a seguir:

@contato.nome = params[:contato][:nome]
@contato.email = params[:contato][:email]
@contato.celular = params[:contato][:celular]

Isso com certeza não seria nada produtivo, pois imagine fazer isso para 10 ou mais atributos? Então usamos a atribuição em massa por fazer assim:

params.require(:contato).permit(:nome, :email, :celular)

Vamos nos concentrar no bloco condicional que o método possui. A operação save retorna true caso o registro seja persistido e false, caso contrário. Se o registro for persistido, o usuário será redirecionado para a página show mostrando os dados do novo usuário cadastrado e uma mensagem 'Contato was successfully created'. Se o método save retornar false a página new será renderizada mostrando os problemas que impediram a persistência.

O método update é muito parecido com o create: a única diferença é a chamada ao método update, passando os parâmetros recebidos pelo método contato_params.

O último é o método destroy, que exclui um contato da base, como mostra a Listagem 2.

Listagem 2. Métodos destroy


def destroy
   @contato.destroy
   redirect_to contatos_url
 end

Ele simplesmente chama o destroy na variável @contato, que foi inicializada antes por set_contato. Depois redireciona para a página de listagem dos contatos (contatos_url), forçando uma atualização da tabela.

Visão/Apresentação

Na pasta agenda/app/views/contato encontraremos pelo menos os seguintes arquivos: index, show, new, edit e _form. Vamos analisar os pontos mais significativos de cada um deles.

Listagem 3. Arquivo index.html.erb

<h1>Listing contatos</h1>
   
  <table>
    <thead>
      <tr>
        <th>Nome</th>
        <th>Email</th>
        <th>Celular</th>
        <th></th>
        <th></th>
        <th></th>
      </tr>
    </thead>
   
    <tbody>
      <% @contatos.each do |contato| %>
        <tr>
          <td><%= contato.nome %></td>
          <td><%= contato.email %></td>
          <td><%= contato.celular %></td>
          <td><%= link_to 'Show', contato %></td>
          <td><%= link_to 'Edit', edit_contato_path(contato) %></td>
          <td><%= link_to 'Destroy', contato, method: :delete, data: { confirm: 'Are you sure?' } %></td>
        </tr>
      <% end %>
    </tbody>
  </table>
   
  <br>
   
  <%= link_to 'New Contato', new_contato_path %>

Repare que temos código HTML misturado com código Ruby. O que está entre as tags <%= %> e <% %> é código Ruby. A diferença entre as duas é que a primeira parte do código colocado será renderizado como HTML (ex: <%= link_to 'Show', contato %>), mas não no caso do código colocado na segunda (ex: <% @contatos.each do |contato| %>). O mais significativo nesse arquivo é o bloco de código que percorre a variável @contatos preenchida no método index do controller.

Vemos também o uso do helper link_to, que é traduzido em tempo de execução para um link HTML comum (<a href=”#” />).

Listagem 4. Arquivo show.html.erb

<p id="notice"><%= notice %></p>
   
  <p>
    <strong>Nome:</strong>
    <%= @contato.nome %>
  </p>
   
  <p>
    <strong>Email:</strong>
    <%= @contato.email %>
  </p>
   
  <p>
    <strong>Celular:</strong>
    <%= @contato.celular %>
  </p>
   
  <%= link_to 'Edit', action: 'edit', id: @contato.id %> |
  <%= link_to 'Back', action: 'index' %>

O arquivo da Listagem 4 é usado pela action show e ele simplesmente apresenta o valor de cada atributo do objeto contato que foi recuperado no método show do controlador que já analisamos. Também “imprime” o atributo notice com a mensagem de sucesso no caso de os métodos create e update executarem sem erros.

Já os arquivos new e edit tem funcionalidades bem parecidas, pois os campos que ambos devem conter são os mesmos. Então para evitar duplicidade de código, seguindo o conceito DRY (Don't Repeat Yourself), o Rails disponibiliza o que é chamado de partials templates, usualmente conhecido apenas como partials. Com isso podemos isolar o código que poderia se repetir apenas em um arquivo, como foi feito com o _form. O nome do arquivo que conterá o partial template inicia com um undescore (_).

assim, o conteúdo do arquivo _form deve ser parecido com o da Listagem 5.

Listagem 5. Arquivo _form

<% if @contato.errors.any? %>
      <div id="error_explanation">
        <ul>
        <% @contato.errors.full_messages.each do |msg| %>
          <li><%= msg %></li>
        <% end %>
        </ul>
      </div>
    <% end %>
   
  <%= form_for(@contato) do |f| %>
    <div class="field">
      <%= f.label :nome %><br>
      <%= f.text_field :nome %>
    </div>
    <div class="field">
      <%= f.label :email %><br>
      <%= f.text_field :email %>
    </div>
    <div class="field">
      <%= f.label :celular %><br>
      <%= f.text_field :celular %>
    </div>
    <div class="actions">
      <%= f.submit %>
    </div>
  <% end %>

O primeiro bloco de código está percorrendo uma lista contendo possíveis erros na validação de campos. Se, por exemplo, decidiu validar o nome de um contato que deve ter entre 10 a 50 caracteres, e um usuário tentou cadastrar um contato com apenas cinco caracteres, o Rails preencherá a variável errors com um erro.

No segundo bloco de código vemos o uso do helper form_for. Ele faz a associação entre cada campo do formulário com os campos do objeto contato (também conhecido como binding).

Os arquivos new e edit fazem uso do arquivo _form por renderizá-lo com a seguinte instrução:

<%= render 'form' %>

Para fazer a chamada ao arquivo não colocamos o _ (underscore) nem a extensão do mesmo, já que o framework cuida disso.

Veja que com Ruby criar um CRUD nunca foi tão fácil! A quantidade de código digitada foi mínima se comparado a outras linguagens.

Espero que tenham gostado e até a próxima!

Referências

Akita On Rails
http://www.akitaonrails.com/

Rails Guide
http://guides.rubyonrails.org/

Documentação Rails
http://www.rubyonrails.com.br/documentacao

Helpers
http://guides.rubyonrails.org/form_helpers.html.