Crie seu primeiro aplicativo de diário com Ruby on Rails

Criado por Piotr Szotkowski (chastell) e Tomasz Stachewicz (tomash)
Tradução PT-BR por (Ricardo Hiroyuki Eihara Junior)

Iremos criar desde o início uma pequena aplicação de votação usando o framework de desenvolvimento web para Ruby chamado Rails. Pense o que sua primeira aplicação deveria ser sobre - pode ser algo simples que inclua uma coleção de algo: por exemplo, uma lista de tarefas, um diário, etc. Usaremos o diário como base neste tutorial.

INSTRUTOR(A): Para saber mais sobre a razão por trás desse tutorial para iniciantes um pouco diferente, dê uma olhada em post.

Certifique-se que você tenha o Rails instalado. Siga o guia de instalação para estar preparado.

Conheça as ferramentas

 

Editor de Texto

  • Atom, Sublime Text, Vim e Emacs são exemplos de editores de texto que você pode usar para escrever seu código e editar arquivos.

 

Terminal (Conhecido como Prompt de Comando no Windows)

  • É onde você inicia o servidor do Rails e executa os comandos.

 

  • (Firefox, Safari, Chrome) para ver sua aplicação.

INSTRUTOR(A): Ajude com a instalação; garanta que o editor de texto esteja propriamente preparado (por exemplo, verifique se o encoding é UTF-8).

Importante

É importante que você siga as instruções específicas para o seu sistema operacional - os comandos que você precisa executar no Windows são um pouco diferentes do que no Mac ou Linux. Se você estiver tendo problemas verifique o Sistema Operacional embaixo dos comandos. No caso de utilizar um serviço cloud (por exemplo, nitrous), você precisa rodar os comandos do Linux mesmo estando em uma máquina com windows.

HTML Puro

Arquivos e Diretórios

Crie um novo diretório (pasta) e crie um arquivo com o nome de index.html nele. Abra o arquivo no seu editor e no navegador.

INSTRUTOR(A): Explique que os navegadores podem abrir arquivos locais, apenas a URL que vai ficar estranha.

Estrutura do HTML

Comece adicionando uma estrutura para o seu HTML escrevendo o que está embaixo no arquivo index.html:

<!doctype html>
<html>
<head>
<title>Minha pequena aplicação web: Programar é mágico!</title>
<meta charset="UTF-8" />
<link rel="stylesheet" href="https://rawgithub.com/krzysztofbialek/Rails-Girls-Warsaw-App/master/style.css" />
</head>
<body>
</body>
</html>

INSTRUTOR(A): Explique as duas partes do HTML, <head> e <body>. Explique a tag <title> e brevemente a tag <meta>, <link> e <script> se necessário. Bootstrap está presente no projeto por isso o CSS pode ser pulado (a menos que os participantes queiram que você explique).

Primeiro conteúdo visto

Adicione o seguinte HTML entre as tags <body> e </body> (sinta-se livre para ajustar o conteúdo…):

<h1>Meu Diário do Rails Girls</h1>
<div>
<h2>Enviei um pedido para o Rails Girls</h2>
<p>1.02.2014</p>
<p>Acabei de preencher um formulário para o workshop da Rails Girls. Mal posso esperar para descobrir se estou dentro! </p>
<h2>Estou dentro!</h2>
<p>15.02.2014</p>
<p>Recebi um email falando que meu pedido foi aceito! Estarei no workshop na próxima semana!</p>
<h2>O primeiro dia começa...</h2>
<p>22.02.2014</p>
<p>Hoje é o primeiro dia do Workshop do Rails Girls. Meu instrutor é um pouco diferente mas aparentemente todos nós estamos com o Rails instalado e podemos agora começar a aprender.</p>
</div>

Estes são seus três primeiros registros no diário. Note como são diferentes as tags mostradas e note a repetição da estrutura.

INSTRUTOR(A): Fale um pouco sobre as tags em HTML e sua semântica.

Mais HTML

Adicione o seguinte seja antes ou depois das entradas do diário acima (denovo, ajuste a seu gosto)

<div>
<h1>My sites favoritos</h1>
<ul>
<li><a href="http://railsgirls.com">Rails Girls</a></li>
<li><a href="https://en.wikibooks.org/wiki/Ruby_Programming">Wikibooks</a></li>
<li><a href="http://guides.rubyonrails.org">Ruby on Rails Guides</a></li>
</ul>
</div>
<img src="http://railsgirls.com/images/rg-warsaw.png" />

Este é uma lista não ordenada em HTML com alguns itens listados nos links com uma referência (URLs) para outras páginas. É seguido de um parágrafo contendo uma imagem - e a origem da imagem esta na URL dada.

INSTRUTOR(A): Explique como a Web funciona e fale um pouco sobre os elementos em HTML e seus atributos.

Aqui está um link de um repositório com um estilo basico para se usar na aplicação.

Mudando para o Rails

INSTRUTOR(A): Se os seus estudantes estão no Windows, considere utilizar o Nitrous.IO como base para as próximas partes.

Nova aplicação em Rails

Abra a janela do terminal (Prompt de Comando do Windows), mude para o diretório onde os seus arquivos estão (usando o comando cd) e rode rails new diary - ele vai levar um tempo e terminará criando uma nova aplicação do Rails. Execute cd diary para mudar para o diretório da aplicação.

INSTRUTOR(A): Explique como navegar pelos diretórios e executar comandos.

Rodando o servidor

Uma vez dentro do diretório diary execute rails server e (quando ele terminar de começar) vá para http://localhost:3000 no seu navegador. Você deverá ver uma página de Bem-vindo a bordo. Pare o servidor pressionando ctrl-c.

INSTRUTOR(A): Explique o que aconteceu e o que são as saídas da janela do terminal. Se o servidor falhar em começar por causa do JavaScript runtime, gem install therubyracer e descomente a linha relevante na Gemfile.

Primeira rota e visão

Crie o controlador e a rota.

Execute rails generate controller welcome index - ele vai gerar seu primeiro controlador e a rota que leva a ele. Inicie seu servidor e vá para http://localhost:3000 para ver que sua aplicação realmente vai para a rota /welcome/index.

Pare o servidor e execute rake routes para ver todas as rotas suportadas pela sua aplicação.

INSTRUTOR(A): Explique as URLs e a hierarquia da URL. Explique como o Rails mapeia as URLs e o que acontece por debaixo dos panos.

Mova a visão para o topo do seu site

Edite o arquivo config/routes.rb e descomente (remove o # da frente) a linha root 'welcome#index' (provavelmente será a sétima linha). Isso vai fazer com que a raiz da sua aplicação seja renderizada pela ação Welcome#index. Vá para http://localhost:3000 e veja que realmente a página principal da sua aplicação agora serve aquele visão (diferente da página Bem-vindo a bordo).

INSTRUTOR(A): Explique como a página principal da aplicação é a raiz da hierarquia das URLs e que é a página que as pessoas visitam quando apenas colocam o nome do hospedeiro na barra de endereço do navegador.

Mova de um HTML existente para a visão certa

Edite o arquivo app/views/welcome/index.html.erb e copie seu conteúdo da tag <body> do seu arquivo original index.html (a sua lista de entradas do diário e links dos sites), trocando as duas linhas (com <h1> e <p>) na visão. Atualize o navegador e veja que a página agora contém o conteúdo correto.

INSTRUTOR(A): Explique que a visão apenas contém o que está em <body> e </body>, e que o resto é comum para toda a aplicação definida em outro lugar.

Iteração

Repetição de conteúdo

Se você olhar como é estruturado sua lista de links, parece que todo item da lista é similar aos outros - ele contém uma URL (o link que será levado quando clicar nele) e um nome (o que deveria ser quando o usuário ver e poder clicar nele). Melhor do que escrever os links em HTML puro (e potencialmente cometer algum erro em algum deles) vamos abstrair um pouco e iterar sobre uma coleção de pares de URLs e nomes.

Troque o conteúdo da tag <ul> com o seguinte:

<%
@websites = [
["http://railsgirls.com", "Rails Girls"],
["https://en.wikibooks.org/wiki/Ruby_Programming", "Wikibooks"],
["http://guides.rubyonrails.org", "Ruby on Rails Guides"],
]
%>
<% for url, name in @websites %>
<li><a href="<%= url %>"><%= name %></a></li>
<% end %>

Atualize o navegador para ver se a página continua com os mesmos links.

INSTRUTOR(A): Explique o que aconteceu - o que é um array, o que <% e <%= tags do ERb significam (e como eles diferem), como a iteração funciona.

Manter o código ou dado (como mostrado acima com o vetor @websites) nas visualizações é simples, mas é uma prática ruim e pode trazer problemas com o decorrer do tempo. Para iniciantes vamos mover o vetor @websites da visão para o controlador. Remove da visão e coloque em app/controllers/welcome_controller.rb no método index para parecer assim:

class WelcomeController < ApplicationController
def index
@websites = [
["http://railsgirls.com", "Rails Girls"],
["https://en.wikibooks.org/wiki/Ruby_Programming", "Wikibooks"],
["http://guides.rubyonrails.org", "Ruby on Rails Guides"],
]
end
end

Note que depois de atualizar o navegador nada deverá mudar - isso é por causa das variáveis que começam com @ (chamadas de variáveis de instancia de variáveis) e podem ser acessadas tanto pela visualização quanto pelo controlador.

INSTRUTOR(A): Explique a conexão entre a ação WelcomeController#index e a visualização; dê ênfase na diferença entre o @ que começa em @websites e puro url e nome.

Crie um modelo

Com os links feitos e escritos na mão, vamos fazer algo com os registros do diário. Dessa vez nós não abusaremos da estrutura do vetor do Ruby, mas com um próprio modelo que represente o dado inserido. Vamos começar gerando o modelo - execute rails generate model Entry title:string date:date contents:text para criar um modelo Entry que representará um registro com título, data de publicação e algum conteúdo.

INSTRUTOR(A): Explique o que são modelos e o que é a notação field:type; explique a diferença entre os tipos string e text se necessário.

Migre o banco de dados

Execute rake db:migrate para migrar o banco de dados para uma tabela com a mesma estrutura dos registros do diário.

INSTRUTOR(A): Explique o que banco de dados são (em termos abstratos, são camadas para armazenar os dados da nossa aplicação e prover as estruturas do modelo) e o porquê são necessárias. Explique que o que estiver na memória não sera persistida por padrão e é preciso explicitamente persistir para que esteja disponível na próxima requisição.

Brinque com o modelo no console do Rails

Agora que temos nosso modelo, podemos começar a criar instancias desse modelo - registros do diário que não estão escritos diretamente na visão HTML. Para isso, vamos aprender uma nova ferramenta: O console do Rails. Comece com rails console e, uma vez que ele execute e mostre para você >>, crie alguns registros:

>> Entry.create "title" => "Submitted a Rails Girls application", "date" => Date.new(2014, 2, 1), "contents" => "Just submitted an application to a Rails Girls workshop. Can’t wait to see whether I’ll get in!"

>> Entry.create "title" => "Got in!", "date" => Date.new(2014, 2, 15), "contents" => "Received an email that my application got accepted! I’ll be at a RG workshop next week!"

>> Entry.create "title" => "The first day starts…", "date" => Date.new(2014, 2, 22), "contents" => "Today is the first day of the Rails Girls workshop. My coach is quite strange but it seems we all have Rails installed now and can start learning."

Note como o console - assim como o rails server - mostra para o você o log do que acontece por trás. Você pode sempre pegar um vetor de todos os registros através de Entry.all.

INSTRUTOR(A): Explique o que está acontecendo. Devagar.

Visualizando o conteúdo persistido

Adicione a instancia do modelo na visualização existente.

Edite a ação WelcomeController#index (no arquivo app/controllers/welcome_controller.rb) e adicione as seguintes linhas antes ou depois das linhas contendo @websites:

@entries = Entry.all

Edite o arquivo app/views/welcome/index.html.erb e substitua as linhas dos registros do diário com o seguinte:

<% for entry in @entries %>
<h2><%= entry.title %></h2>
<p><%= entry.date %></p>
<p><%= entry.contents %></p>
<% end %>

INSTRUTOR(A): Discuta sobre o que acontece, qual é a ordem dos registros e como podem ser re-ordenados (por exemplo, data inversa) e onde deve acontecer essa lógica

Crie um controlador para os registros do diário

Agora que temos um modelo, precisamos criar um controlador para cuidar das ações relacionadas com as intancias do modelo (criar novos registros, mostrar, editar e deletar registros existentes). Execute rails generate controller Entries - isso deve gerar a classe EntriesController. Verifique rake routes - e note que apenas o controlador não basta, precisamos ainda apontar as URLs para as ações do controlador.

Edit config/routes.rb e adicione a linha resources "entries" em algum lugar dentro do block de Diary::Application.routes.draw. Execute rake routes novamente: note que agora sua aplicação terá várias novas rotas.

INSTRUTOR(A): Explique como as rotas do Rails funcionam e como ele fazem para as URLs criadas e mapeadas para as ações do controlador por padrão.

Uma visão de todas os registros

Como pode ser visto na saída de rake routes, as URLs são ligadas com os suas relativas ações do controlador. Vamos ver o que falta - visite http://localhost:3000/entries no seu navegador. Uh-oh, parece que a ação index está faltando - vamos adicioná-lo - abra app/controllers/entries_controller.rb e adicione o método vazio abaixo dentro da definição da classe:

def index
end

Agora atualize o seu navegador - nós não temos mais um problema unknow action, temos agora um problema template is missing. Salve o arquivo sem nenhum conteúdo como app/views/entries/index.html.erb (note que é igual ao arquivo index.html.erb da pasta welcome de antes, mas desta vez será na pasta entries) e atualize seu navegador novamente - ele deverá agora mostrar uma página vazia. Isso é bom, pois nossa visualização está vazia no momento.

INSTRUTOR(A): Explique como as ações são renderizadas e relacionadas com as visualizações por padrão.

Agora vá para o arquivo app/controllers/welcome_controller.rb e encontre o método WelcomeController#index (aquele que começa com def index). Encontre a linha que atribue a variável @entries (deve começar com @entries =) e copie-o para ENtriesController#index (e então para o método index de EntriesController, que pode ser encontrado em app/controller/welcome_controller.rb). Similarmente, vá para a visualização app/views/welcome/index.html.erb e copie o block @entries.each (todos dentro das linha indentadas inclusive o end) para app/views/entries/index.html.erb. Atualize o navegador: ele deverá agora mostrar todos os registros do seu diário.

INSTRUTOR(A): Explique que mesmo isso pareça ter pouco progresso, existe uma mudança significativa: nós não mais estamos operando dentro do contexto da página principal, mas sim de uma lista de registro do diário apenas (sem os links para os outros sites, por exemplo).

Uma visualização de um único registro

Note como, quando você execute rake routes, a saída diz que o padrão de /entries/:id(.:format) mapeia para a ação do controlador de entries#show. Vá para http://localhost:3000/entries/1 - a URL para seu primeiro registro do diário; note como nós, novamente, estamos faltando com a ação em EntriesController. Adicione a ação, então atualize o navegador e adicione depois também a visualização que está também faltando.

INSTRUTOR(A): Guie na adição da ação que falta e da visualização se necessário; tenha certeza que o processo (desde decifrar descobrir a rota certa em rake routes) está bem entendido.

Agora, vamos descobrir como interpretar o 1 do fim da URL para mostrar o registro certo. Crie a ação EntriesController#show assim:

def show
@entry = Entry.find(params["id"])
end

Esta linha significa ‘pegue o parametro id e o use para o método Entry.find para encontrar o registro certo’. Agora edite a visualização app/views/entries/show.html.erb e coloque o seguinte:

<h2><%= @entry.title %></h2>
<p><%= @entry.date %></p>
<p><%= @entry.contents %></p>

Visite http://localhost:3000/entries/1 e compare com http://localhost:3000/entries/2 para ver que usando params['id'] significa que diferentes registro do diário são mostrados.

INSTRUTOR(A): Explique a parte do :id de rake routes é feito para ser uma chave para o hash params; discuta o que mais pode ter no hash params.

Ligando registros

Execute rake routes novamente; note como a linha da ação entries#show começa com um entry no prefixo da coluna. Vá para app/views/entries/index.html.erb e mude a linha responsável para mostrar o título para o abaixo:

<h2><%= link_to(entry.title, entry_path(entry)) %></h2>

Note como utilizamos o método link_to que tem dois parametros, o texto para ser mostrado (entry.title) e o caminho do link. Verifique a origem da página para ver o caminho dos títulos seguintes. Note como o caminha é criado pela chamado do método entry_path com entry como argumento.

INSTRUTOR(A): Lembre como os links de HTML se parecem. Explique a relação entre entry_path e o prefixo entry do rake routes. Explique como o método entry_path precisa do argumento entry. Explique o que o método entry_url faz (e como ele difere do método entry_path) se você quiser.

Agora vamos voltar para a página indice com todos os registros: edit app/views/entries/show.html.erb e adicione o link para os registros do indice, assim:

<p><%= link_to("Back to all entries", entries_path) %></p>

Note, novamente, como o prefixo entries de rake routes é utilizado para construir o nome pelo método entries_path. Note como o método não precisa de parametro.

Criando registros pela IU

Adicionando o formulário novo registro

Agora que nós temos um modo de mostrar a lista com todos os registros e mostrar apenas um registro, vamos adicionar um modo de criar novos registros do diário. Execute rake routes e tente descobrir qual URL (e ação) é responsável pela criação do novo registro.

Vá para o indice de todos os registros e adicione o link para criar um novo registro:

<%= link_to("New entry", new_entry_path) %>

Clique no link - e adicione a ação que falta e a visualização.

INSTRUTOR(A): Garanta que este processo é bem entendido até aqui.

Edite a visualização app/views/entries/new.html.erb e cole o abaixo:

<%= form_for(Entry.new) do |form| %>
<p><%= form.label("title") %></p>
<p><%= form.text_field("title") %></p>
<p><%= form.label("contents") %></p>
<p><%= form.text_area("contents") %></p>
<p><%= form.submit %></p>
<% end %>
<p><%= link_to("Back to all entries", entries_path) %></p>

Nota: podemos pular os labels por agora

INSTRUTOR(A): Mostre como o HTML é produzido pelo helper form_for e tente explicar como ele funciona.

Manipulando o formulário de ‘novo registro’

Atualize o navegador e tente adicionar um novo registro - você deverá o conhecido erro unknow action. Adicione a ação em EntriesController, mas para iniciantes vamos mostrar o que a ação recebe:

def create
render(:text => params.inspect)
end

Atualize o navegador e inspecione o que exatamente a ação recebe de params.

INSTRUTOR(A): Explique como preencher um campo de texto e uma area de texto e submeter o formulário acaba com todos os params sendo enviados (post) para a ação do controlador. Explique o que .inspect faz.

Criando e persistindo um novo registro

Edite a ação create e faça parecer assim:

def create
entry_params = params["entry"]
entry = Entry.create(entry_params)
redirect_to(entry_path(entry))
end

Note como tentamos pegar os parametros do novo registro(seu título e conteúdo) do hash params e então criamos um novo registro a partir dele, assim como no console. Tente submeter o formulário novamente - note como ainda não conseguimos, pois recebemos ActiveModel::ForbiddenAttributesError.

Esse erro é por causa de ações de segurança - é relativamente simples de fazer um POST com quaisquer parametros que o usuário quer, e Rails nos protege de um usuário desonesto que quer setar parametros que ele não deveria setar (como id). Precisamos declarar quais parametros o usuário consegue mudar; mude a primeira linha de create para:

entry_params = params["entry"].permit("title", "contents")

Agora tente submeter o formulário novamente - deste vez deverá funcionar e você deverá ser redirecionado para um novo registro recém-criado.

INSTRUTOR(A): Tenha certeza que a parte dos parametros do novo registro está bem claro e que os campos aceitos devem ser permitidos explicitamente.

Editando pela IU

Adicionando um formulário de edição de registro

Agora que podemos visualizar os registros criados, vamos também adicionar uma opção para editá-los. Execute rake routes e tente adivinhar qual rota é responsável pela edição do registro.

INSTRUTOR(A): Garanta que tudo está claro até aqui.

Edite a visualização app/views/entries/show.html.erb e adicione a linha abaixo em algum lugar:

<p><%= link_to("Edit this entry", edit_entry_path(@entry)) %></p>

Atualize a visualização do registro e clique no link. Adicione a ação que falta e a visualização também.

INSTRUTOR(A): Novamente, tenha certeza que tudo está claro.

Vamos primeiro garantir que nossa ação edit expõe o registro certo para a visualização. Garanta que a ação edit pareça igual a ação show - isto é, pegue o registro baseado no id que vem da URL:

def edit
@entry = Entry.find(params["id"])
end

Agora copie o conteúdo da visualização app/views/entries/new.html.erb para app/views/entries/edit.html.erb, mas mude a primeira linha para que o formulário deste registro em particular - e opcionalmente, adicione um link para voltar na visualização do registro:

<%= form_for(@entry) do |form| %>
<p><%= form.label("title") %></p>
<p><%= form.text_field("title") %></p>
<p><%= form.label("contents") %></p>
<p><%= form.text_area("contents") %></p>
<p><%= form.submit %></p>
<% end %>
<p><%= link_to("Back to this entry", entry_path(@entry)) %></p>
<p><%= link_to("Back to all entries", entries_path) %></p>

INSTRUTOR(A): Garanta que isto esteja claro.

Agora tente submeter o formulário - que ação está faltando? Cria no controlador:

def update
entry_params = params["entry"].permit("title", "contents")
entry = Entry.find(params["id"])
entry.update(entry_params)
redirect_to(entry_path(entry))
end

Verifique se tudo isso funciona e se você pode editar os registros.

INSTRUTOR(A): Garanta que a ação update e seu conteúdo estejam claros e foram entedidos - desde permitir os parametros através dos registros até redirecionar para o caminho certo.

Ideias para o futuro

Brinque com sua aplicação! Aqui estão algumas ideias que você queira adicionar: