Veja mais em rubyonrails.org: Mais Ruby on Rails

Começando com Rails

Este guia aborda a instalação e a execução do Ruby on Rails.

Depois de ler este guia, você vai saber:

1 Premissas do Guia

Este guia é projetado para iniciantes que desejam começar uma aplicação Rails do zero. Ele não assume que você tenha nenhuma experiência anterior com Rails.

O Rails é um framework para aplicações web que é executado em cima da linguagem de programação Ruby. Se você não tem nenhuma experiência com Ruby, você vai achar a curva de aprendizado bastante íngrime começando direto com Rails. Existem diversas listas organizadas de materiais online para aprender Ruby:

Fique atento que alguns materiais, apesar de excelentes, envolvem versões antigas do Ruby chegando a 1.6, e frequentemente 1.8, e não incluem parte da sintaxe que você vai ver no seu dia-a-dia desenvolvendo com Rails.

2 O que é o Rails?

Rails é um framework de desenvolvimento de aplicações web escrito na linguagem de programação Ruby. Foi projetado para facilitar o desenvolvimento de aplicações web, criando premissas sobre tudo que uma pessoa desenvolvedora precisa para começar. Permite que você escreva menos código, enquanto realiza mais do que em muitas outras linguagens ou frameworks. Pessoas desenvolvedoras experientes em Rails, também dizem que desenvolver aplicações web ficou mais divertido.

Rails é um software opinativo. Assumindo que há uma "melhor" maneira para fazer as coisas, e foi projetado para encorajar essa maneira - e, em alguns casos para desencorajar alternativas. Se você aprender o "Rails Way", provavelmente terá um grande aumento de produtividade. Se você insistir nos velhos hábitos de outras linguagens, tentando usar os padrões que você aprendeu em outro lugar, você pode ter uma experiência menos feliz.

A filosofia do Rails possui dois princípios fundamentais:

  • Não repita a si mesmo: DRY (don't repeat yourself) é um conceito de desenvolvimento de software que estabelece que "Todo conhecimento deve possuir uma representação única, de autoridade e livre de ambiguidades em todo o sistema.". Ao não escrever as mesmas informações repetidamente, o código fica mais fácil de manter, de expandir e com menos bugs.
  • Convenção sobre configuração: O Rails possui convenções sobre as melhores maneiras de fazer muitas coisas em uma aplicação web, devido a essas convenções você não precisa especificar detalhes através de arquivos de configuração infinitos.

3 Criando um Novo Projeto em Rails

A melhor forma de ler esse guia é seguir o passo à passo. Todos os passos são essenciais para rodar a aplicação de exemplo e nenhum código ou passos adicionais serão necessários.

Seguindo este guia, você irá criar um projeto em Rails chamado de blog, um weblog (muito) simples. Antes de você começar a construir a aplicação, você precisa ter certeza de ter o Rails instalado.

Os exemplos à seguir usam $ para representar seu prompt de terminal em um sistema operacional baseado em UNIX, mesmo que ele tenha sido customizado para parecer diferente. Se você está utilizando Windows, seu prompt será parecido com algo como c:\source_code>

3.1 Instalando o Rails

Antes de você instalar o Rails, você deve validar para ter certeza que seu sistema tem os pré requisitos necessários instalados. Esses incluem Ruby e SQLite3.

Abra o prompt de linha de comando. No macOS abra o Terminal.app, no Windows escolha executar no menu inicial e digite 'cmd.exe'. Qualquer comando que antecede o sinal de dólar $ deverá ser rodado em linha de comando. Verifique se você tem a versão atual do Ruby instalado:

$ ruby -v
ruby 2.5.0

O Rails necessita da versão Ruby 2.5.0 ou mais atual. Se o número da versão retornada for menor que este número, você precisará instalar uma versão do Ruby mais atual.

Para instalar o Ruby e o Ruby on Rails mais rápido no seu sistema operacional Windows, você pode usar o Rails Installer. Para mais informações de instalação de outros Sistemas Operacionais, dê uma olhada em ruby-lang.org.

Se você está utilizando o Windowns, você deve também instalar o Ruby Installer Development Kit.

Você também precisará instalar o banco de dados SQLite3. Muitos sistemas operacionais populares semelhantes ao UNIX são fornecidos com uma versão compatível do SQLite3. No Windows, se você instalou o Rails pelo instalador do Rails, você já possui o SQLite instalado. Você também podem achar mais instruções de instalação em SQLite3 website. Verifique se está corretamente instalado e no seu PATH

$ sqlite3 --version

O programa deverá reportar sua versão.

Para instalar o Rails, use o comando gem install fornecido pelo RubyGems:

$ gem install rails

Para verificar se você tem tudo instalado corretamente, você deve rodar o comando à seguir:

$ rails --version

Se esse comando retornar algo como "Rails 6.0.0", você está pronto para continuar.

3.2 Criando a Aplicação Blog

O Rails vem com vários scripts chamados generators que são projetados para tornar sua vida de desenvolvedor fácil, criando tudo que é necessário para começar a trabalhar em uma tarefa em particular. Um desses é o generator de nova aplicação, que irá te fornecer a base de uma nova aplicação em Rails para que você não precise escrever tudo sozinho.

Para utilizar esse generator, abra um terminal, navegue para um diretório onde você tenha permissão para criar arquivos, e digite:

$ rails new blog

Este comando irá criar uma aplicação em Rails chamada Blog em um diretório blog e irá instalar as dependências das gems que já foram mencionadas no Gemfile usando bundle install.

Se você está utilizando um subsistema Windows para Linux então existem algumas limitações nas notificações dos arquivos do sistema que significa que você deve desabilitar as gems spring e listen, o que poderá ser feito rodando o comando rails new blog --skip-spring --skip-listen.

Você pode ver todas as opções de linha de comando que a aplicação Rails aceita rodando o comando rails new -h.

Depois de criar a aplicação blog, entre em sua pasta:

$ cd blog

A pasta blog tem vários arquivos auto-gerados e pastas que compõem a estrutura de uma aplicação Rails. A maior parte da execução deste tutorial será feito na pasta app, mas à seguir teremos um resumo básico das funções de cada um dos arquivos e pastas que o Rails gerou por padrão:

Arquivo/Pasta Objetivo
app/ Contém os controllers, models, views, helpers, mailers, channels, jobs, e assets para sua aplicação. Você irá se concentrar nesse diretório pelo restante desse guia.
bin/ Contém o script do Rails que inicializa sua aplicação e contém outros scripts que você utiliza para configurar, atualizar, colocar em produção ou executar sua aplicação.
config/ Configure as rotas, banco de dados entre outros de sua aplicação. Este conteúdo é abordado com mais detalhes em Configuring Rails Applications.
config.ru Configuração Rack para servidores baseados em Rack usados para iniciar a aplicação. Para mais informações sobre o Rack, consulte Rack website.
db/ Contém o schema do seu banco de dados atual, assim como as migrations do banco de dados.
Gemfile
Gemfile.lock
Esses arquivos permitem que você especifique quais dependências de gem são necessárias na sua aplicação Rails. Esses arquivos são usados pela gem Bundler. Para mais informações sobre o Bundler, acesse o website do Bundler.
lib/ Módulos extendidos da sua aplicação.
log/ Arquivos de log da aplicação.
package.json Este arquivo permite que você especifique quais dependências npm são necessárias para sua aplicação Rails. Este arquivo é usado pelo Yarn. Para mais informações do Yarn, acesse o website do Yarn.
public/ O único diretório visto pelo mundo. Contém arquivos estáticos e assets compilados.
Rakefile Este arquivo localiza e carrega tarefas que podem ser rodadas por linhas de comando. As tarefas são definidas nos componentes do Rails. Ao invés de editar o Rakefile, você deve criar suas próprias tarefas adicionando os arquivos no diretório lib/tasks da sua aplicação.
README.md Este é um manual de instruções para sua aplicação. Você deve editar este arquivo para informar o que seu aplicativo faz, como configurá-lo e assim por diante.
storage/ Arquivos de armazenamento ativo do serviço de disco. Mais informações em Active Storage Overview.
test/ Testes unitários, fixtures, e outros tipos de testes. Mais informações em Testing Rails Applications.
tmp/ Arquivos temporários (como cache e arquivos pid).
vendor/ Diretório com todos os códigos de terceiros. Em uma típica aplicação Rails inclui vendored gems.
.gitignore Este arquivo diz ao Git quais arquivos (ou padrões) devem ser ignorados. Acesse GitHub - Ignoring files para mais informações sobre arquivos ignorados.
.ruby-version Este arquivo contém a versão padrão do Ruby.

4 Olá, Rails!

Para começar vamos colocar um texto na tela rapidamente. Para fazer isso, você precisa que seu servidor de aplicação Rails esteja em execução.

4.1 Inicializando o Servidor Web

Você já tem uma aplicação Rails funcional. Para vê-la você deve iniciar um servidor web em sua máquina de desenvolvimento. Você pode fazer isso executando o seguinte comando no diretório blog:

$ rails server

Se você está usando Windows, deve executar os scripts do diretório bin para o interpretador do Ruby: ruby bin\rails server.

A compressão de assets JavaScript requer que você tenha um executor disponível em seu sistema operacional. Na ausência de um executor você verá um erro de execjs durante a compilação dos assets. Geralmente o macOS e o Windows possuem um executor JavaScript instalado por padrão. therubyrhino é o executor recomendado para usuários de JRuby e vem no Gemfile por padrão em aplicações geradas com JRuby. Você pode avaliar todos executores em ExecJS.

A execução do comando irá iniciar o Puma, um servidor web distribuído com o Rails por padrão. Para ver sua aplicação em execução, abra um navegador e navegue para http://localhost:3000. Você deve ver a página padrão com informações do Rails:

Captura de tela escrito Yay! Você está no Rails(em inglês)

Para interromper a execução do servidor Web, pressione Ctrl+C na janela do terminal em que o servidor está sendo executado. Para verificar se o servidor realmente foi interrompido, você deve ver o cursor do prompt novamente. Para a maioria dos sistemas baseados em UNIX, incluindo o macOS, o cursor é representando por um sinal de $. Em modo de desenvolvimento, o Rails geralmente não exige que você reinicie o servidor; mudanças feitas nos arquivos da aplicaçnao serão automaticamente aplicadas no servidor.

A página de "Yay! Você está no Rails! (Yay! You're on Rails!)" é o smoke test (teste de sanidade) para uma nova aplicação Rails: garante que o seu software esteja configurado corretamente, o suficiente para gerar uma página.

4.2 Diga "Olá", Rails

Para que o Rails diga "Olá", você precisa criar no mínimo um controller e uma view.

O objetivo de um controller é receber requisições específicas para a aplicação. O Routing (roteamento) decide qual controller recebe quais requisições. Muitas vezes, há mais de uma rota para cada controller, e diferentes rotas podem ser providas por diferentes actions. O objetivo de cada action é coletar informações para fornecer para uma view.

O objetivo de uma view é exibir essas informações em um formato legível para humanos. Uma diferença importante a ser feita é que é no controller, não na view, onde as informações são coletadas. A view deve apenas exibir essas informações. Por padrão, os templates de view são escritos em uma linguagem chamada eRuby (Embedded Ruby) que é processada pelo ciclo da requisição no Rail antes de ser enviada para o usuário.

Para criar um novo controller, você precisará executar o gerador de controller e informar que você deseja um controller chamado "Welcome" com uma action chamada "index", exatamente assim:

$ rails generate controller Welcome index

O Rails criará vários arquivos e uma rota para você.

create  app/controllers/welcome_controller.rb
 route  get 'welcome/index'
invoke  erb
create    app/views/welcome
create    app/views/welcome/index.html.erb
invoke  test_unit
create    test/controllers/welcome_controller_test.rb
invoke  helper
create    app/helpers/welcome_helper.rb
invoke    test_unit
invoke  assets
invoke    scss
create      app/assets/stylesheets/welcome.scss

Os mais importantes são, certamente, o controller, localizado em app/controllers/welcome_controller.rb e a view, localizada em app/views/welcome/index.html.erb.

Abra o arquivo app/views/welcome/index.html.erb em seu editor de texto. Exclua todo o código existente no arquivo e substitua pela linha de código abaixo:

<h1>Olá, Rails!</h1>

4.3 Configuração da Página Inicial da Aplicação

Agora que criamos o controller e a view, precisamos informar ao Rails quando queremos que "Olá, Rails" seja exibido. No nosso caso, queremos que seja exibido quando navegarmos para a URL raiz de nosso site, http://localhost:3000. No momento, "Yay! Você está no Rails!" é que está preenchendo esse lugar.

Em seguida, você deve informar ao Rails onde está localizada a sua página inicial.

Abra o arquivo config/routes.rb em seu editor de texto.

Rails.application.routes.draw do
  get 'welcome/index'

  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

Este é o arquivo routing (roteamento) da sua aplicação que contém as entradas em um DSL (domain-specific language) especial que informa ao Rails como conectar requisições de entrada com controllers e actions. Edite este arquivo adicionando a linha de código root 'welcome#index'. Deve ser algo parecido com o seguinte:

Rails.application.routes.draw do
  get 'welcome/index'

  root 'welcome#index'
end

root 'welcome#index' informa ao Rails para mapear as requisições para a raiz da aplicação para o controller "welcome", action "index" e get 'welcome/index' informa ao Rails para mapear as requisições para http://localhost:3000/welcome/index para o controller "welcome", action "index". Isso foi criado anteriormente quando você executou o gerador de controller (rails generate controller Welcome index).

Inicie o servidor web novamente se você o interrompeu para gerar o controller (rails server) e navegue até http://localhost:3000 no seu navegador. Você verá a mensagem "Olá, Rails!", a mesma que você colocou em app/views/welcome/index.html.erb, indicando que essa nova rota de fato vai para a action index de WelcomeController e está renderizando a view corretamente.

Para mais informações sobre roteamento, consulte Roteamento do Rails de Fora para Dentro.

5 Iniciando e Executando

Agora que você já viu como criar um controller, uma action, e uma view, vamos criar algo um pouco mais relevante.

Na aplicação do Blog você irá criar um novo resource. Resource é um termo utilizado para uma coleção de objetos similares, como artigos, pessoas ou animais. Você pode criar, visualizar, editar e deletar dados de um resource e essas ações são definidas como operações CRUD.

O Rails te fornece um método resources que pode ser usado para declarar um recurso padrão REST. Você precisa adicionar o article resource no config/routes.rb e o arquivo ficará como a seguir:

Rails.application.routes.draw do
  get 'welcome/index'

  resources :articles

  root 'welcome#index'
end

Se você executar rails routes, você verá que foram definidas rotas para todas as actions padrão RESTful. O significado do prefixo da coluna (e de outras colunas) será visto mais adiante, mas por enquanto, observe que o Rails entende article de forma singular e faz o uso significativo da distinção.

$ rails routes
       Prefix Verb   URI Pattern                  Controller#Action
welcome_index GET    /welcome/index(.:format)     welcome#index
     articles GET    /articles(.:format)          articles#index
              POST   /articles(.:format)          articles#create
  new_article GET    /articles/new(.:format)      articles#new
 edit_article GET    /articles/:id/edit(.:format) articles#edit
      article GET    /articles/:id(.:format)      articles#show
              PATCH  /articles/:id(.:format)      articles#update
              PUT    /articles/:id(.:format)      articles#update
              DELETE /articles/:id(.:format)      articles#destroy
         root GET    /                            welcome#index

Na próxima seção, você adicionará a funcionalidade para criar e visualizar novos artigos (articles) em sua aplicação. Este é o "C" e o "R" do CRUD: create (criação) e read (leitura). O formulário para fazer isso ficará assim:

The new article form

Por enquanto está um pouco simples, mas tudo bem. Nós iremos melhorar o estilo mais adiante.

5.1 Preparando a base

Primeiramente, você precisa de um lugar na aplicação para criar um novo artigo. Um ótimo lugar seria em /articles/new. Com a rota já definida, agora é possível fazer requisições para /articles/new na aplicação. Acesse http://localhost:3000/articles/new e você verá um erro de rota:

Another routing error, uninitialized constant ArticlesController

Este erro ocorre porque a rota precisa ter um controller definido para atender à requisição. A solução para esse problema específico é simples: crie um controller chamado ArticlesController. Você pode fazer isso executando este comando:

$ rails generate controller Articles

Se você abrir o recém-criado app/controllers/articles_controller.rb verá um controller vazio:

class ArticlesController < ApplicationController
end

Um controller é uma classe definida para herdar de ApplicationController. É dentro dessa classe que você define os métodos que se tornarão as ações desse controller. Essas ações executarão operações CRUD nos artigos em nosso sistema.

Existem métodos public, private e protected no Ruby, mas apenas métodos public podem ser ações nos controllers. Para mais detalhes, consulte Programação Ruby.

Se você atualizar http://localhost:3000/articles/new agora, receberá um novo erro:

Unknown action new for ArticlesController!

Este erro indica que o Rails não consegue encontrar a ação new dentro do ArticlesController que você acabou de gerar. Isso ocorre porque quando os controllers são gerados no Rails, eles estão vazios por padrão, a menos que você diga as ações que deseja durante o processo de geração.

Para definir manualmente uma ação dentro de um controller, tudo o que você precisa fazer é definir um novo método dentro do controller. Abra app/controllers/articles_controller.rb e, dentro da classe ArticlesController, defina o método new para que agora seu controller fique assim:

class ArticlesController < ApplicationController
  def new
  end
end

Com o método new definido em ArticlesController, se você atualizar http://localhost:3000/articles/new verá um outro erro:

Template is missing for articles/new

Você está recebendo esse erro agora porque o Rails espera que actions como esta tenham views associadas a elas para exibir suas informações. Sem uma view disponível, o Rails gerará uma exceção.

Vamos ver a mensagem de erro completa novamente:

ArticlesController#new está faltando um template para o formato da requisição: text/html

NOTA! Como dito, o Rails espera que uma ação renderize um template com o mesmo nome, contido em uma pasta com o nome de seu controller. Se esse controller for uma API que responde com 204 (sem conteúdo), e que não requer um template, então esse erro ocorrerá ao tentar acessá-lo pelo navegador pois esperamos que um template HTML seja renderizado para essas requisições. Se esse for o caso, continue.

A mensagem identifica qual template está ausente. Nesse caso, é o template articles/new. O Rails procurará primeiro esse template. Se não for encontrado, ele tentará carregar um template chamado application/new, porque o ArticlesController herda do ApplicationController.

Em seguida, a mensagem contém request.formats que especifica o formato do template a ser exibido em resposta. Ele está definido como text/html, conforme solicitamos esta página pelo navegador, portanto o Rails está procurando um template HTML.

O template mais simples que funcionaria nesse caso seria o localizado em app/views/articles/new.html.erb. A extensão desse nome de arquivo é importante: a primeira extensão é o formato do template e a segunda extensão é o handler (tratadores) que será usado para renderizar o template. O Rails está tentando encontrar um template chamado articles/new em app/views para a aplicação. O formato para este template pode ser apenas html e o handler padrão para HTML é erb. O Rails usa outros handlers para outros formatos. O handler de builder é usado para criar templates XML e o handler de coffee usa o CoffeeScript para criar templates JavaScript. Como você deseja criar um novo formulário HTML, você usará a linguagem ERB projetada para incorporar Ruby em HTML.

Portanto, o arquivo deve se chamar articles/new.html.erb e precisa estar localizado dentro do diretório app/views da aplicação.

Agora vá em frente e crie um novo arquivo em app/views/articles/new.html.erb e escreva este conteúdo:

<h1>New Article</h1>

Ao atualizar http://localhost:3000/articles/new você verá que a página tem um título. A rota, o controller, a action e a view estão funcionando harmoniosamente! É hora de criar o formulário para um novo artigo.

5.2 O primeiro formulário

Para criar um formulário com este template, você vai usar um construtor de formulário (form builder). O construtor de formulários do Rails é disponibilizado por um método helper chamado form_with. Para utilizá-lo, adicione o código abaixo no arquivo app/views/articles/new.html.erb:

<%= form_with scope: :article, local: true do |form| %>
  <p>
    <%= form.label :title, 'Título' %><br>
    <%= form.text_field :title %>
  </p>

  <p>
    <%= form.label :text, 'Texto' %><br>
    <%= form.text_area :text %>
  </p>

  <p>
    <%= form.submit, 'Enviar' %>
  </p>
<% end %>

Se você atualizar a página, você vai ver o mesmo formulário do nosso exemplo acima. Construir formulários no Rails é simples assim!

Quando você chama o form_with, você passa para ele a identificação do escopo para esse formulário. Neste caso, o symbol :article. Isso mostra ao form_with para qual escopo é o formulário em questão. Dentro do bloco deste método, o objeto FormBuilder - representado pelo form - é usado para construir dois títulos e dois campos textos, um para cada título (labels) e texto (text fields) de um artigo. Finalmente, uma chamada ao submit do objeto form criará um botão de submissão do formulário.

Porém, existe um problema com este formulário. Se você inspecionar o HTML que foi gerado, visualizando o código-fonte da página, você verá que o atributo action do formulário está apontando para /articles/new. Isso é um problema porque esta rota vai exatamente para a mesma página que você está no momento, e essa rota deveria ser usada apenas para exibir o formulário para um novo artigo.

O formulário precisa usar uma URL diferente para que consigar ir para outro lugar. Isso pode ser feito de forma simples com a opção :url do form_with. Normalmente, no Rails, a action que é usada para novas submissões de formulários é chamada "create", então o formulário precisa apontar para esta ação.

Altere a linha do form_with dentro de app/views/articles/new.html.erb para ficar da seguinte forma:

<%= form_with scope: :article, url: articles_path, local: true do |form| %>

Neste exemplo, o helper articles_path é passado para a opção :url. Para saber o que o Rails fará com isso, veja novamente o resultado do rails routes:

$ rails routes
      Prefix Verb   URI Pattern                  Controller#Action
welcome_index GET    /welcome/index(.:format)     welcome#index
     articles GET    /articles(.:format)          articles#index
              POST   /articles(.:format)          articles#create
  new_article GET    /articles/new(.:format)      articles#new
 edit_article GET    /articles/:id/edit(.:format) articles#edit
      article GET    /articles/:id(.:format)      articles#show
              PATCH  /articles/:id(.:format)      articles#update
              PUT    /articles/:id(.:format)      articles#update
              DELETE /articles/:id(.:format)      articles#destroy
         root GET    /                            welcome#index

O helper articles_path diz ao Rails para apontar o formulário para o padrão de URI associado com o prefixo articles; e o formulário vai (por padrão) enviar uma requisição POST para esta rota. Isto esta associado com a action create do controller atual, no caso o ArticlesController.

Com o formulário e sua rota definidos, você será capaz de preencher o formulário e clicar no botão submit para iniciar o processo de criação de um novo artigo, então vá em frente e faça isso. Quando você enviar, você verá um erro familiar:

Unknown action create for ArticlesController

Agora você precisa adicionar a action create dentro do ArticlesController para que isso funcione.

Por padrão o form_with submete formulários usando Ajax, evitando assim o redirecionamento da página inteira. Para tornar este guia mais fácil de entender, por ora, nós desabilitamos isso ao utilizar o local: true.

5.3 Criando artigos

Para fazer este "Unknown action" desaparecer, você pode definir a action create dentro da classe ArticlesController em app/controllers/articles_controller.rb, logo após a action new, como vemos a seguir:

class ArticlesController < ApplicationController
  def new
  end

  def create
  end
end

Se você re-enviar o formulário agora, você ainda não verá nenhuma mudança na página. Não se preocupe! Isto é porque, por padrão, o Rails retorna a resposta 204 No Content para uma action se não especificarmos qual resposta ele deve retornar. Nós apenas adicionamos a action create mas não especificamos nada sobre como a resposta deve ser. Neste caso, a action create deve salvar nosso novo artigo no banco de dados.

Quando um formulário é enviado, os campos do formulário são enviados ao Rails como parameters (parâmetros). Estes parâmetros podem ser então referenciados dentro das actions do controller, normalmente para executar uma tarefa específica. Para ver como estes parâmetros são, altere a action create para isto:

def create
  render plain: params[:article].inspect
end

O método render está recebendo uma simples hash com a chave :plain e o valor params[:article].inspect. O método params é o objeto que representa os parâmetros (ou campos) enviados pelo formulário. O método params retorna um objeto ActionController::Parameters, que permite você acessar as chaves do hash usando strings ou symbols. Neste caso, os únicos parâmetros que importam são os recebidos pelo formulário.

Certifique-se de ter um bom entendimento sobre o método params, pois você o usará frequentemente. Vamos considerar uma URL de exemplo: http://www.example.com/?username=dhh&[email protected]. Nesta URL, o params[:username] será igual a "dhh" e o params[:email] será igual a "[email protected]".

Se você reenviar o formulário novamente, você verá algo parecido com isso:

<ActionController::Parameters {"title"=>"First Article!", "text"=>"This is my first article."} permitted: false>

A action agora está exibindo os parâmetros para o artigo que estão sendo enviados pelo formulário. Porém, isto não é realmente útil. Sim, você pode ver os parâmetros mas nada em particular está sendo feito com eles.

5.4 Criando um model para o Article (artigo, inglês)

O model, no rails, utiliza o nome no singular, e a sua tabela correspondente no banco de dados utiliza o nome no plural. O Rails fornece um gerador para criar models via linha de comando, o que é utilizado pela maioria das pessoas desenvolvedoras na hora de criar novos models.

Para criar um model, execute a linha de comando abaixo:

$ rails generate model Article title:string text:text

Com esse comando nós dizemos ao Rails que queremos criar um model chamado Article, com um atributo chamado title do tipo string, e um atributo text do tipo text. Esses atributos serão automaticamente adicionados à tabela articles no banco de dados, e mapeadas no model Article.

O Rails irá criar um monte de arquivos. Por enquanto, nós estamos apenas interessados no app/models/article.rb e db/migrate/20140120191729_create_articles.rb (o nome pode ficar um pouco diferente). O último é responsável por criar a estrutura do banco de dados, o que é a próxima coisa que iremos olhar.

O Active Record é inteligente o suficiente para automaticamente mapear o nome das colunas para os atributos do model, o que significa que você não precisa declará-los dentro do model, já que o Active Record faz automaticamente.

5.5 Executando uma Migration

Como acabamos de ver, rails generate model cria um arquivo database migration no diretório db/migrate. Migrations são classes ruby designadas para simplificar a criação e modificação de tabelas no banco de dados. O Rails usa comandos rake para executar a migration, e é possível desfazê-la depois de ser aplicada ao seu banco de dados. Os nomes dos arquivos das Migrations incluem um timestamp ("carimbo de hora" em inglês) para assegurar que elas serão processadas na ordem em que foram criadas.

Se você olhar no arquivo db/migrate/YYYYMMDDHHMMSS_create_articles.rb (lembre-se, o seu pode ter o nome um pouco diferente), você encontrará:

class CreateArticles < ActiveRecord::Migration[6.0]
  def change
    create_table :articles do |t|
      t.string :title
      t.text :text

      t.timestamps
    end
  end
end

A migration acima cria um método chamado change que será chamado quando esta migration for executada. A ação definida neste método também é reversível, o que significa que o Rails sabe como reverter a alteração, caso você queira. Quando você executar esta migration, será criada uma tabela articles com uma coluna do tipo string e uma coluna do tipo text. Também serão criados dois campos de timestamp, para permitir que o Rails monitore quando um Article for criado ou atualizado.

Para mais informações sobre migrations, veja Active Record Migrations.

Agora, você pode usar um comando rails para executar a migration:

$ rails db:migrate

O Rails irá executar este comando de migration e dizer que a tabela de Articles foi criada.

==  CreateArticles: migrating ==================================================
-- create_table(:articles)
   -> 0.0019s
==  CreateArticles: migrated (0.0020s) =========================================

Por padrão você está trabalhando em ambiente de desenvolvimento, por este motivo, este comando irá aplicar a migration no banco de dados definido na seção development do seu arquivo config/database.yml. Se você quiser executar as migrations em outro ambiente, em produção por exemplo, você deve especificar quando chamar o comando: rails db:migrate RAILS_ENV=production

5.6 Salvando dados no Controller

De volta no ArticlesController, precisamos alterar a action create para utilizar o novo model Article para salvar os dados no banco de dados. Abra app/controllers/articles_controller.rb e altere a action create da seguinte forma:

def create
  @article = Article.new(params[:article])

  @article.save
  redirect_to @article
end

O que está acontecendo: todo model Rails pode ser inicializado com seus respectivos atributos, os quais são automaticamente mapeados para suas respectivas colunas no banco de dados. Na primeira linha fazemos apenas isso (lembre-se que params[:article] contém os atributos que estamos interessados). Então, @article.save é responsável por salvar o model no banco de dados. Finalmente, redirecionaremos o usuário para action show, que definiremos mais tarde.

Você deve estar pensando qual o motivo de usar A maiúsculo no Article.new, quando a maior parte deste guia usou letra minúscula. Neste contexto, estamos nos referindo a classe Article definida no arquivo app/models/article.rb. Nome de classe em Ruby deve começar sempre com letra maiúscula.

Como veremos mais tarde, @article.save retorna um booleano indicando se o article foi salvo ou não.

Se você for agora para http://localhost:3000/articles/new você quase será capaz de criar um article. Tente! Você verá um erro parecido com esse:

Forbidden attributes for new article

O Rails tem diversas features de segurança que te ajudam a construir uma aplicação segura, e você está lidando com uma delas agora. Esta se chama strong parameters (parâmetros fortes), que nos obrigam a dizer ao Rails exatamente quais parâmetros estão permitidos nas actions do controller.

Por que é necessário? A habilidade de salvar todos os parâmetros do controller no model de uma só vez deixa o trabalho de quem está programando mais fácil, mas essa conveniência pode ser utilizada de forma maliciosa. E se um request para o servidor for trabalhado para fazer parecer um novo formulário de article, mas também incluir campos extras com valores que violam a integridade da sua aplicação? Isso possibilitaria a entrada/atribuição de dados ruins no seu model e banco de dados junto com dados bons - podendo, potencialmente, quebrar sua aplicação ou algo pior.

Nós definimos quais são os parâmetros permitidos para prevenir atribuições de dados indesejados. Nesse caso, queremos permitir e exigir os parâmetros title e text para uso válido do create. A sintaxe para isso utiliza require e permit. A alteração envolverá uma linha na action create.

  @article = Article.new(params.require(:article).permit(:title, :text))

Frequentemente refatoramos o código para trazer esta alteração para dentro do seu próprio método, para que possa ser reutilizado por diferentes actions no mesmo controller, por exemplo create e update. Além do problema de atribuição de dados inconsistentes, o método frequentemente é colocado como privado, para termos certeza que não será possível chamá-lo fora do seu contexto. Aqui está o resultado:

def create
  @article = Article.new(article_params)

  @article.save
  redirect_to @article
end

private
  def article_params
    params.require(:article).permit(:title, :text)
  end

Para mais informações, veja a referência acima e este artigo sobre Strong Parameters.

5.7 Mostrando Artigos

Se você enviar o formulário novamente agora, o Rails reclamará por não encontrar a action show. Isso não é muito útil, então vamos adicionar a action show antes de proceder.

Como vimos na exibição de rails routes, a rota para a actionshow é do seguinte modo:

article GET    /articles/:id(.:format)      articles#show

A sintaxe especial :id diz ao Rails que esta rota espera um parâmetro :id, que no nosso caso será o id (identificador) do artigo.

Como fizemos antes, precisamos adicionar a action show no app/controllers/articles_controller.rb e sua respectiva view.

Uma prática frequente é colocar as ações CRUD padrão em cada controller na seguinte ordem: index,show, new,edit, create,update e destroy. Você pode usar qualquer ordem que escolher, mas lembre-se de que esses são métodos públicos; conforme mencionado anteriormente neste guia, eles devem ser colocados antes de declarar visibilidade private no controller.

Dado isso, vamos adicionar a action show, da seguinte maneira:

class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])
  end

  def new
  end

Algumas coisas a serem observadas. Usamos Article.find para encontrar o artigo que nos interessa, passando params[:id] para obter o parâmetro :id da requisição. Também usamos uma variável de instância (prefixada com @) para conter uma referência ao objeto do artigo. Fazemos isso porque o Rails passará todas as variáveis de instância para a view.

Agora, crie um novo arquivo app/views/articles/show.html.erb com o seguinte conteúdo:

<p>
  <strong>Título:</strong>
  <%= @article.title %>
</p>

<p>
  <strong>Texto:</strong>
  <%= @article.text %>
</p>

Com essa mudança, você deve finalmente conseguir criar novos artigos. Visite http://localhost:3000/articles/new e faça uma tentativa!

Imagem mostrando artigos

5.8 Listando todos os artigos

Nós ainda precisamos de um jeito para listar todos os nossos artigos, então vamos fazer isso. A rota para isso, conforme apresentado pela execução do comando rails routes, é:

articles GET    /articles(.:format)          articles#index

Adicione a action index correspondente a essa rota dentro do ArticlesController no arquivo app/controllers/articles_controller.rb. Quando escrevemos uma action index, a prática usual é colocá-lo como o primeiro método no controller. Vamos fazer isso:

class ArticlesController < ApplicationController
  def index
    @articles = Article.all
  end

  def show
    @article = Article.find(params[:id])
  end

  def new
  end


E então, finalmente, adicione a view para essa action, localizada em app/views/articles/index.html.erb:

<h1>Listing Articles</h1>

<table>
  <tr>
    <th>Title</th>
    <th>Text</th>
    <th></th>
  </tr>

  <% @articles.each do |article| %>
    <tr>
      <td><%= article.title %></td>
      <td><%= article.text %></td>
      <td><%= link_to 'Show', article_path(article) %></td>
    </tr>
  <% end %>
</table>

Agora se você acessar http://localhost:3000/articles você verá uma lista de todos os artigos que você criou.

Você pode agora criar, mostrar e listar artigos. Agora vamos adicionar links para navegação pelas páginas.

Abra o arquivo app/views/welcome/index.html.erb e modifique-o da seguinte maneira:

<h1>Olá, Rails!</h1>
<%= link_to 'Meu Blog', controller: 'articles' %>

O método link_to é um dos view helpers (auxiliares de exibição) internos do Rails. Isso cria um hiperlink com base no texto a ser exibido e para onde ir - nesse caso, para o caminho dos artigos.

Vamos adicionar links para as outras views também, começando com a adição deste link de "Novo artigo" em app/views/articles/index.html.erb, colocando-o acima da tag <table>:

<%= link_to 'Novo artigo', new_article_path %>

Este link permitirá abrir o formulário para criar um novo artigo.

Agora, adicione outro link em app/views/articles/new.html.erb, abaixo do formulário para retornar à action index:

<%= form_with scope: :article, url: articles_path, local: true do |form| %>
  ...
<% end %>

<%= link_to 'Voltar', articles_path %>

Por fim, adicione um link à view app/views/articles/show.html.erb para voltar também à action `index ', assim as pessoas que estão visualizando um único artigo podem voltar e exibir a lista inteira novamente:

<p>
  <strong>Título:</strong>
  <%= @article.title %>
</p>

<p>
  <strong>Texto:</strong>
  <%= @article.text %>
</p>

<%= link_to 'Voltar', articles_path %>

Se você deseja vincular a uma action no mesmo controller, não precisa especificar a opção :controller, pois o Rails usará o controller atual por padrão.

No modo de desenvolvimento (que é o que você está trabalhando por padrão), o Rails recarrega sua aplicação com todas as solicitações do navegador, portanto, não é necessário parar e reiniciar o servidor web quando uma alteração for feita.

5.10 Adicionando Algumas Validações

O arquivo de model app/models/article.rb é o mais simples possível:

class Article < ApplicationRecord
end

Não há muito nesse arquivo - mas observe que a classe Article herda de ApplicationRecord. ApplicationRecord herda de ActiveRecord::Base que fornece uma grande funcionalidade aos seus models do Rails gratuitamente, incluindo operações básicas de CRUD (Create (Criar), Read (Ler), Update (Atualizar), Destroy (Remover)) do banco de dados, validação de dados, bem como suporte sofisticado à pesquisa e capacidade de relacionar vários models entre si.

O Rails inclui métodos para ajudá-lo a validar os dados que você envia aos models. Abra o arquivo app/models/article.rb e edite-o:

class Article < ApplicationRecord
  validates :title, presence: true,
                    length: { minimum: 5 }
end

Essas alterações garantirão que todos os artigos tenham um título com pelo menos cinco caracteres. O Rails pode validar uma variedade de condições em um model, incluindo a presença ou exclusividade de colunas, seu formato e a existência de objetos associados. As validações são abordadas em detalhes em Active Record Validations](active_record_validations.html).

Com a validação agora em atividade, quando você chama @article.save em um artigo inválido, a expressão retornará false. Se você abrir o arquivo app/controllers/articles_controller.rb novamente, você notará que não verificamos o resultado da chamada @article.save dentro da action create. Se @article.save falhar nessa situação, precisamos mostrar o formulário de volta ao usuário. Para fazer isso, altere as ações new ecreate dentro app/controllers/articles_controller.rb para estes:

def new
  @article = Article.new
end

def create
  @article = Article.new(article_params)

  if @article.save
    redirect_to @article
  else
    render 'new'
  end
end

private
  def article_params
    params.require(:article).permit(:title, :text)
  end

A ação new agora está criando uma nova variável de instância chamada @article, e você verá por que isso ocorre em apenas alguns momentos.

Observe que dentro da ação create usamos render em vez de redirect_to quando o save retorna false. O método render é usado para que o objeto @article seja enviado de volta para a view new quando ela é renderizada. Esta renderização é feita dentro da mesma solicitação que o envio do formulário, enquanto o redirect_to dirá ao navegador para realizar outra solicitação.

Se você recarregar http://localhost:3000/articles/new e tentar salvar um artigo sem título, o Rails te enviará de volta ao formulário, mas isso não é muito útil. Você precisa dizer ao usuário que algo deu errado. Para fazer isso, você pode modificar o arquivo app/views/articles/new.html.erb para verificar se há mensagens de erro:

<%= form_with scope: :article, url: articles_path, local: true do |form| %>

  <% if @article.errors.any? %>
    <div id="error_explanation">
      <h2>
        <%= pluralize(@article.errors.count, "erro") %> não permitiram
        este artigo de ser salvo:
      </h2>
      <ul>
        <% @article.errors.full_messages.each do |msg| %>
          <li><%= msg %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <p>
    <%= form.label :title, 'Título' %><br>
    <%= form.text_field :title %>
  </p>

  <p>
    <%= form.label :text, 'Texto' %><br>
    <%= form.text_area :text %>
  </p>

  <p>
    <%= form.submit, 'Enviar' %>
  </p>

<% end %>

<%= link_to 'Voltar', articles_path %>

Algumas coisas estão acontecendo. Verificamos se há algum erro com @article.errors.any? e, nesse caso, mostramos uma lista de todos erros com @article.errors.full_messages.

pluralize é um auxiliar que recebe um número e uma string como argumentos. Se o número for maior que um, a sequência será automaticamente pluralizada.

A razão pela qual adicionamos @article = Article.new no ArticlesController é que, caso contrário, @article seria nil em nossa view, e chamar @article.errors.any? geraria um erro.

O Rails agrupa automaticamente os campos que contêm um erro com uma div com a classe field_with_errors. Você pode definir uma regra CSS destacá-los.

Agora você receberá uma boa mensagem de erro ao tentar salvar um artigo sem título no nosso formulário de criação de artigo http://localhost:3000/articles/new:

Form With Errors

5.11 Atualizando Artigos

Cobrimos a parte "CR" do CRUD. Agora vamos nos concentrar na parte "U", atualizando artigos.

O primeiro passo que vamos dar é adicionar uma ação edit ao ArticlesController, geralmente entre as ações new ecreate:

def new
  @article = Article.new
end

def edit
  @article = Article.find(params[:id])
end

def create
  @article = Article.new(article_params)

  if @article.save
    redirect_to @article
  else
    render 'new'
  end
end

A view conterá um formulário semelhante ao que usamos para criar novos artigos. Crie um arquivo chamado app/views/articles/edit.html.erb e deixe-o como mostrado em seguida:

<h1>Edit Article</h1>

<%= form_with(model: @article, local: true) do |form| %>

  <% if @article.errors.any? %>
    <div id="error_explanation">
      <h2>
        <%= pluralize(@article.errors.count, "error") %> prohibited
        this article from being saved:
      </h2>
      <ul>
        <% @article.errors.full_messages.each do |msg| %>
          <li><%= msg %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <p>
    <%= form.label :title %><br>
    <%= form.text_field :title %>
  </p>

  <p>
    <%= form.label :text %><br>
    <%= form.text_area :text %>
  </p>

  <p>
    <%= form.submit %>
  </p>

<% end %>

<%= link_to 'Back', articles_path %>

Desta vez, apontamos o formulário para a action update, que ainda não foi definida, mas em breve será.

Passar o objeto artigo para o método form_with definirá automaticamente o URL para enviar o formulário do artigo editado. Esta opção informa ao Rails que o formulário deve ser enviado pelo método HTTP PATCH, que é o método HTTP que espera ser utilizado para o update dos nossos resources de acordo com o protocolo REST.

Além disso, passando um model para form_with, como model: @article na view edit acima, fará com que os helpers do form preencham os campos com os valores correspondentes do objeto. Passando um symbol de scope como scope: :article, como foi feito na nova view, apenas exibirá os campos do formulário vazios. Mais detalhes podem ser encontrados em Documentação form_with.

Agora, precisamos criar a action update no controller app/controllers/articles_controller.rb. Adicione-a entre os metodos create e private:

def create
  @article = Article.new(article_params)

  if @article.save
    redirect_to @article
  else
    render 'new'
  end
end

def update
  @article = Article.find(params[:id])

  if @article.update(article_params)
    redirect_to @article
  else
    render 'edit'
  end
end

private
  def article_params
    params.require(:article).permit(:title, :text)
  end

O novo método, update, é usado quando você deseja atualizar um registro que já existe, ele aceita um hash contendo os atributos que você deseja atualizar. Como antes, se houve um erro ao atualizar o artigo, queremos mostrar o formulário de volta ao usuário.

Nós podemos reutilizar o método article_params que definimos anteriormente para a action create

Não é necessário passar todos os atributos para o update. Por exemplo, se @article.update(title: 'A new title') for chamado, o Rails vai apenas atualizar o atributo title, deixando todos os outros atrubutos como estavam.

Finalmente, nós queremos mostrar o link para a action edit na lista dos artigos, então vamos adiciona-lo agora no arquivo app/views/articles/index.html.erb para aparecer logo após o link de "Show":

<table>
  <tr>
    <th>Title</th>
    <th>Text</th>
    <th colspan="2"></th>
  </tr>

  <% @articles.each do |article| %>
    <tr>
      <td><%= article.title %></td>
      <td><%= article.text %></td>
      <td><%= link_to 'Show', article_path(article) %></td>
      <td><%= link_to 'Edit', edit_article_path(article) %></td>
    </tr>
  <% end %>
</table>

E também iremos adicionar em app/views/articles/show.html.erb, assim podemos ter um link para "Edit" na página de um artigo. Adicione no fim do seu modelo:

...

<%= link_to 'Edit', edit_article_path(@article) %> |
<%= link_to 'Back', articles_path %>

E aqui como nossa aplicação está até agora:

Índice action com link edit

5.12 Usando partials para limpar duplicações em views

Nossa página edit se parece muito com a página new; na verdade, ambas compartilham o mesmo código para exibir o formulário. Vamos remover esta duplicação usando uma view partial. Por convenção, arquivos de partials são prefixados com um underline.

Você pode ler mais sobre partials no guia Layouts e Renderização no Rails.

Crie um novo arquivo app/views/articles/_form.html.erb com o conteudo a seguir:

<%= form_with model: @article, local: true do |form| %>

  <% if @article.errors.any? %>
    <div id="error_explanation">
      <h2>
        <%= pluralize(@article.errors.count, "error") %> prohibited
        this article from being saved:
      </h2>
      <ul>
        <% @article.errors.full_messages.each do |msg| %>
          <li><%= msg %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <p>
    <%= form.label :title %><br>
    <%= form.text_field :title %>
  </p>

  <p>
    <%= form.label :text %><br>
    <%= form.text_area :text %>
  </p>

  <p>
    <%= form.submit %>
  </p>

<% end %>

Tudo exceto a declaração form_with permaneceu igual. A razão pela qual podemos usar a declaração form_with mais curta e simples para substituir qualquer outro formulário é que @article é um resource correspondente a um conjunto completo de rotas RESTful, e o Rails pode inferir qual URI e método a ser usado. Para obter mais informações sobre esse uso do form_with, consulte Estilo orientado a recursos.

Agora, vamos atualizar a view app/views/articles/new.html.erb para usar a nova partial, reescrevendo-a completamente:

<h1>New Article</h1>

<%= render 'form' %>

<%= link_to 'Back', articles_path %>

Depois, faremos o mesmo em app/views/articles/edit.html.erb:

<h1>Edit Article</h1>

<%= render 'form' %>

<%= link_to 'Back', articles_path %>

5.13 Deletando Artigos

Nós estamos prontos para cobrir a parte "D" de um CRUD, remover artigos da base de dados. Seguindo a convenção REST, a rota para deletar artigos, de acordo com o retorno do comando rails routes é:

DELETE /articles/:id(.:format)      articles#destroy

O método delete roteado deve ser usado para rotas que destroem recursos. Se esta ação for deixada em uma simples rota get, pode ser possível que pessoas criem urls maliciosas como esta:

<a href='http://example.com/articles/1/destroy'>look at this cat!</a>

Utilizamos o método delete para destruir recursos, e essa rota é mapeada à ação destroy dentro deapp/controllers/articles_controller.rb, que ainda não existe. O método destroy é geralmente a última ação CRUD no o controller e, como as outras ações públicas de CRUD, ele deve ser colocado antes de qualquer método private ouprotected. Vamos adicioná-lo:

def destroy
  @article = Article.find(params[:id])
  @article.destroy

  redirect_to articles_path
end

O ArticlesController completo em app/controllers/articles_controller.rb se parece com isso:

class ArticlesController < ApplicationController
  def index
    @articles = Article.all
  end

  def show
    @article = Article.find(params[:id])
  end

  def new
    @article = Article.new
  end

  def edit
    @article = Article.find(params[:id])
  end

  def create
    @article = Article.new(article_params)

    if @article.save
      redirect_to @article
    else
      render 'new'
    end
  end

  def update
    @article = Article.find(params[:id])

    if @article.update(article_params)
      redirect_to @article
    else
      render 'edit'
    end
  end

  def destroy
    @article = Article.find(params[:id])
    @article.destroy

    redirect_to articles_path
  end

  private
    def article_params
      params.require(:article).permit(:title, :text)
    end
end

Você pode chamar o destroy nos objetos do Active Record quando desejar excluí-los do banco de dados. Observe que não precisamos adicionar uma visualização para esta action, pois estamos redirecionando para a ação index.

Por fim, adicione um link "Destroy" no template da sua action index (app/views/articles/index.html.erb) para agrupar todos juntos.

<h1>Listing Articles</h1>
<%= link_to 'New article', new_article_path %>
<table>
  <tr>
    <th>Title</th>
    <th>Text</th>
    <th colspan="3"></th>
  </tr>

  <% @articles.each do |article| %>
    <tr>
      <td><%= article.title %></td>
      <td><%= article.text %></td>
      <td><%= link_to 'Show', article_path(article) %></td>
      <td><%= link_to 'Edit', edit_article_path(article) %></td>
      <td><%= link_to 'Destroy', article_path(article),
              method: :delete,
              data: { confirm: 'Are you sure?' } %></td>
    </tr>
  <% end %>
</table>

Aqui estamos usando o link_to de uma maneira diferente. Passamos a rota nomeada como segundo argumento e, em seguida, as opções como outro argumento. As opções method: :delete e data: { confirm: 'Are you sure?' } são usadas como atributos HTML5, portanto quando o link é clicado, o Rails primeiro mostra uma caixa de diálogo de confirmação para o usuário e, em seguida, envia o link com o método delete. Isso é feito através do Arquivo JavaScript rails-ujs, que é automaticamente incluído no layout da aplicação (app/views/layouts/application.html.erb) quando foi gerado. Sem esse arquivo, a caixa de diálogo de confirmação não será exibida.

Dialogo de Confirmação

Aprenda mais sobre JavaScript discreto no guia Trabalhando Com JavaScript Com Rails.

Parabêns, agora você pode criar, mostrar, listar, atualizar e destruir artigos. Congratulations, you can now create, show, list, update, and destroy articles.

Em geral, o Rails encoraja usar o método resources em objetos, ao invés de declarar as rotas manualmente. Para mais informação sobre roteamento, veja Roteamento do Rails de Dentro à Fora.

6 Adicionando um Segundo Model

É hora de adicionar um segundo model à aplicação. O segundo model vai lidar com comentários em artigos.

6.1 Gerando um Model

Nós veremos o mesmo generator que usamos antes quando criamos o model Article (artigo, inglês). Desta vez vamos criar um model Comment (comentário) que contém a referência para um artigo. Rode esse comando no seu terminal:

$ rails generate model Comment commenter:string body:text article:references

Este comando vai gerar quatro arquivos:

Arquivo Propósito
db/migrate/20140120201010_create_comments.rb Migration para criar a tabela de comentários no seu banco de dados (o nome incluirá um timestamp diferente)
app/models/comment.rb O model Comment
test/models/comment_test.rb Aparelhagem de testes para o model de comentário
test/fixtures/comments.yml Exemplo de comentários para uso em testes

Primeiro, veja o arquivo app/models/comment.rb:

class Comment < ApplicationRecord
  belongs_to :article
end

Isso é muito semelhante ao model Article que vimos antes. A diferença está na linha belongs_to : article, o que configura uma associação no Active Record. Você vai aprender um pouco sobre associações na próxima seção deste guia.

A palavra-chave (:references) usada no comando bash é um tipo especial de dado para models. Ela cria uma nova coluna na tabela do banco de dados com o nome fornecido ao model anexada a um _id que contém um valor do tipo integer. Para compreender melhor, analise o arquivo db/schema.rb depois de rodar a migration.

Além do model, o Rails também gerou a migration para criar a tabela correspondente no banco de dados:

class CreateComments < ActiveRecord::Migration[6.0]
  def change
    create_table :comments do |t|
      t.string :commenter
      t.text :body
      t.references :article, null: false, foreign_key: true

      t.timestamps
    end
  end
end

A linha t.references cria uma coluna com valores do tipo integer chamada article_id, um índice para ela e uma restrição de chave estrangeira (foreign key) que aponta para a coluna id da tabela articles. Vá em frente e rode a migration:

$ rails db:migrate

O Rails é inteligente o suficiente para executar somente as migrações que ainda não foram rodadas no banco de dados atual, assim neste caso você verá:

==  CreateComments: migrating =================================================
-- create_table(:comments)
   -> 0.0115s
==  CreateComments: migrated (0.0119s) ========================================

6.2 Associando Models

Associações do Active Record permitem declarar facilmente a relação entre dois models. No caso de comentários e artigos, você poderia descrever a relação da seguinte maneira:

  • Cada comentário pertece a um artigo.
  • Um artigo pode possuir muitos comentários.

De fato, essa sintaxe é muito similar à utilizada pelo Rails para declarar essa associação. Você já viu a linha de código dentro do model Comment (app/models/comment.rb) que faz com que cada comentário pertença a um Artigo:

class Comment < ApplicationRecord
  belongs_to :article
end

Você vai precisar editar o arquivo app/models/article.rb para adicionar o outro lado da associação:

class Article < ApplicationRecord
  has_many :comments
  validates :title, presence: true,
                    length: { minimum: 5 }
end

Estas duas declarações habilitam uma boa parte de comportamento automático. Por exemplo, se você possui uma instância da variável @article que contém um artigo, você pode recuperar todos os comentários pertencentes àquele artigo na forma de um array usando @article.comments.

Para mais informações sobre associações do Active Record, veja o guia Associações no Active Record.

6.3 Adicionando a Rota para Comentários

Da mesma forma que o controller welcome, nós vamos precisar adicionar a rota para que o Rails saiba para onde queremos navegar para encontrar comments. Abra o arquivo config/routes.rb novamente e o edite da seguinte maneira:

resources :articles do
  resources :comments
end

Isso cria comments como um recurso aninhado (nested resource) dentro de article. Essa é outra parte do processo para recuperar as relações hierárquicas que existem entre artigos e comentários.

Para mais informações sobre rotas, veja o guia Roteamento no Rails

6.4 Gerando um Controller

Com o model em mãos, você pode voltar sua atenção para a criação do controller correspondente. Mais uma vez, você vai usar o generator usado anteriormente:

$ rails generate controller Comments

Isso cria quatro arquivos e um diretório vazio:

Arquivo/Diretório Propósito
app/controllers/comments_controller.rb O controller de comentários
app/views/comments/ Views do controller são armazenadas aqui
test/controllers/comments_controller_test.rb O teste para o controller
app/helpers/comments_helper.rb Arquivo de helpers da view
app/assets/stylesheets/comments.scss Cascading style sheet (CSS) para o controller

Como em qualquer blog, nossos leitores vão criar seus comentários diretamente depois de lerem o artigo e, uma vez que adicionarem o comentário, serão enviados de volta para a página show do artigo para verem o comentário agora listado. Por essa razão, nosso CommentsController está aqui para fornecer um método que cria comentários e deleta comentários spam quando chegarem.

Então, primeiro nós vamos ligar o show template para Artigos (app/views/articles/show.html.erb) para que possamos criar um novo comentários:

<p>
  <strong>Title:</strong>
  <%= @article.title %>
</p>

<p>
  <strong>Text:</strong>
  <%= @article.text %>
</p>

<h2>Add a comment:</h2>
<%= form_with(model: [ @article, @article.comments.build ], local: true) do |form| %>
  <p>
    <%= form.label :commenter %><br>
    <%= form.text_field :commenter %>
  </p>
  <p>
    <%= form.label :body %><br>
    <%= form.text_area :body %>
  </p>
  <p>
    <%= form.submit %>
  </p>
<% end %>

<%= link_to 'Edit', edit_article_path(@article) %> |
<%= link_to 'Back', articles_path %>

Isso adiciona na página show do Article um formulário que cria um novo comentário chamando a action create no CommentsController. O form_with aqui usa um array que vai construir uma rota aninhada, como /articles/1/comments.

Vamos ligar a action create em app/controllers/comments_controller.rb:

class CommentsController < ApplicationController
  def create
    @article = Article.find(params[:article_id])
    @comment = @article.comments.create(comment_params)
    redirect_to article_path(@article)
  end

  private
    def comment_params
      params.require(:comment).permit(:commenter, :body)
    end
end

Você verá um pouco mais de complexidade aqui do que no controller para artigos. Esse é o efeito colateral do aninhamento que você configurou. Cada requisição para um comentário deve lembrar o artigo ao qual o comentário está anexado, para que a chamada inicial do método find do model Article encontre o artigo em questão.

Além disso, o código aproveita-se de alguns métodos disponíveis para uma associação. Nós usamos o método create em @article.comments para criar e salvar um comentário. Isso vai automaticamente conectar o comentário para que ele pertença àquele artigo em particular.

Uma vez que temos um novo comentário, nós enviamos o usuário de volta ao artigo original usando o helper article_path(@article). Como já vimos anteriormente, isso chama a action show do ArticlesController que por sua vez renderiza o template show.html.erb. É aqui que queremos que o comentário apareça, então vamos adicionar isso ao arquivo app/views/articles/show.html.erb.

<p>
  <strong>Title:</strong>
  <%= @article.title %>
</p>

<p>
  <strong>Text:</strong>
  <%= @article.text %>
</p>

<h2>Comments</h2>
<% @article.comments.each do |comment| %>
  <p>
    <strong>Commenter:</strong>
    <%= comment.commenter %>
  </p>

  <p>
    <strong>Comment:</strong>
    <%= comment.body %>
  </p>
<% end %>

<h2>Add a comment:</h2>
<%= form_with(model: [ @article, @article.comments.build ], local: true) do |form| %>
  <p>
    <%= form.label :commenter %><br>
    <%= form.text_field :commenter %>
  </p>
  <p>
    <%= form.label :body %><br>
    <%= form.text_area :body %>
  </p>
  <p>
    <%= form.submit %>
  </p>
<% end %>

<%= link_to 'Edit', edit_article_path(@article) %> |
<%= link_to 'Back', articles_path %>

Agora podemos adicionar artigos e comentários ao seu blog e mostrá-los nos lugares certos.

Article with Comments

7 Refatorando

Agora que nossos artigos e comentários funcionam, dê uma olhada no template app/views/articles/show.html.erb. Ele está ficando longo e esquisito. Nós podemos usar partials (views parciais) para melhorá-lo.

7.1 Renderizando Coleções de Partials

Primeiramente, nós vamos criar uma partial para extrair a exibição de todos os comentários para o artigo. Crie o arquivo app/views/comments/_comment.html.erb e insira o código a seguir:

<p>
  <strong>Commenter:</strong>
  <%= comment.commenter %>
</p>

<p>
  <strong>Comment:</strong>
  <%= comment.body %>
</p>

Então você pode mudar app/views/articles/show.html.erb para o seguinte código:

<p>
  <strong>Title:</strong>
  <%= @article.title %>
</p>

<p>
  <strong>Text:</strong>
  <%= @article.text %>
</p>

<h2>Comments</h2>
<%= render @article.comments %>

<h2>Add a comment:</h2>
<%= form_with(model: [ @article, @article.comments.build ], local: true) do |form| %>
  <p>
    <%= form.label :commenter %><br>
    <%= form.text_field :commenter %>
  </p>
  <p>
    <%= form.label :body %><br>
    <%= form.text_area :body %>
  </p>
  <p>
    <%= form.submit %>
  </p>
<% end %>

<%= link_to 'Edit', edit_article_path(@article) %> |
<%= link_to 'Back', articles_path %>

Isso fará com que a partial seja renderizada em app/views/comments/_comment.html.erb uma vez para cada comentário na coleção @article.comments. Como o método render itera sobre a coleção @article.comments, ele designa cada comentário para uma variável local nomeada como a partial, nesse caso comment, que então fica disponível para ser exibida na partial.

7.2 Renderizando um Formulário com Partial

Agora vamos mover aquela nova seção de comentários para sua própria partial. Novamente, crie o arquivo app/viewscomments/_form.html.erb contendo:

<%= form_with(model: [ @article, @article.comments.build ], local: true) do |form| %>
  <p>
    <%= form.label :commenter %><br>
    <%= form.text_field :commenter %>
  </p>
  <p>
    <%= form.label :body %><br>
    <%= form.text_area :body %>
  </p>
  <p>
    <%= form.submit %>
  </p>
<% end %>

Então deixe o arquivo app/views/articles/show.html.erb assim:

<p>
  <strong>Title:</strong>
  <%= @article.title %>
</p>

<p>
  <strong>Text:</strong>
  <%= @article.text %>
</p>

<h2>Comments</h2>
<%= render @article.comments %>

<h2>Add a comment:</h2>
<%= render 'comments/form' %>

<%= link_to 'Edit', edit_article_path(@article) %> |
<%= link_to 'Back', articles_path %>

O segundo render apenas define o template de partial que queremos renderizar, comments/form. O Rails é inteligente o suficiente para entender a barra nessa string e perceber que você quer renderizar o arquivo _form.html.erb no diretório app/views/comments.

O objeto @article está disponível para todas as partials renderizadas na view porque o definimos como uma variável de instância.

8 Deletando Comentários

Outra importante feature de um blog é excluir comentários de spam. Para fazer isto, nós precisamos implementar um link de alguma view e uma action destroy no CommentsController.

Primeiro, vamos adicionar o link delete na partial app/views/comments/_comment.html.erb:

<p>
  <strong>Autor do comentário:</strong>
  <%= comment.commenter %>
</p>

<p>
  <strong>Comentário:</strong>
  <%= comment.body %>
</p>

<p>
  <%= link_to 'Destruir comentário', [comment.article, comment],
               method: :delete,
               data: { confirm: 'Você tem certeza?' } %>
</p>

Clicar neste novo link "Destruir comentário" será disparado um DELETE /articles/:article_id/comments/:id ao nosso CommentsController, que pode ser usar isso para encontrar o comentário que queremos excluir, então vamos adicionar uma ação destroy ao nosso controller (app/controllers/comments_controller.rb):

class CommentsController < ApplicationController
  def create
    @article = Article.find(params[:article_id])
    @comment = @article.comments.create(comment_params)
    redirect_to article_path(@article)
  end

  def destroy
    @article = Article.find(params[:article_id])
    @comment = @article.comments.find(params[:id])
    @comment.destroy
    redirect_to article_path(@article)
  end

  private
    def comment_params
      params.require(:comment).permit(:commenter, :body)
    end
end

A action destroy vai encontrar o artigo que estamos vendo, localizar o comentário na collection @article.comments, removê-lo do seu banco de dados e nos enviar de volta para a action show do artigo.

8.1 Excluindo objetos associados

Se você excluir um artigo, os comentários (comments) associados também precisarão ser excluídos, caso contrário, eles simplesmente ocupariam espaço no banco de dados. O Rails permite que você use a opção dependent de uma associação para conseguir isso. Modifique o Modelo de artigo (article), app/models/article.rb, da seguinte forma:

class Article < ApplicationRecord
  has_many :comments, dependent: :destroy
  validates :title, presence: true,
                    length: { minimum: 5 }
end

9 Segurança

9.1 Autenticação Básica

Se fosse fosse publicar o seu blog online, qualquer um poderia adicionar, editar e deletar seus artigos ou comentários.

O Rails disponibiliza um sistema de autenticação HTTP simples que irá funcionar tranquilamente nesta situação.

No ArticlesController nós precisamos que tenha um meio de bloquear o acesso à várias ações se uma pessoa não estiver autenticada. Aqui podemos usar o método http_basic_authenticate_with, que permite o acesso para a ação requisitada se o método deixar.

Para usar o sistema de autenticação, nós especificamos no topo do nosso ArticlesController em app/controllers/articles_controller.rb. No nosso caso, nós queremos que o usuário esteja autenticado em todas as ações, exceto index e show, então nós colocamos isso:

class ArticlesController < ApplicationController

  http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show]

  def index
    @articles = Article.all
  end

  # snippet for brevity

Nós também queremos autorizar somente usuários autenticados a deletar comentários, então em CommentsController (app/controllers/comments_controller.rb) nós colocamos:

class CommentsController < ApplicationController

  http_basic_authenticate_with name: "dhh", password: "secret", only: :destroy

  def create
    @article = Article.find(params[:article_id])
    # ...
  end

  # snippet for brevity

Agora se você tentar criar um novo artigo, você deverá preencher um formulário de autenticação:

Formulário de Autenticação

Outros métodos de autenticação estão disponíveis para aplicações Rails. Dois add-ons de autenticação populares para Rails são o Devise e o Authlogic entre outros.

9.2 Outras Considerações de Segurança

Segurança, especialmente em aplicações web, é uma area ampla e detalhada. O tópico de segurança aplicações Rails é coberto com mais detalhes em Guia de Segurança Ruby on Rails.

10 O que vem depois?

Agora que você criou sua primeira aplicação Rails, sinta-se à vontade para atualizar e experimentar por conta própria.

Lembre-se, você não precisa fazer tudo sem ajuda. Se você precisa de assistência para começar a desenvolver com Rails, sinta-se à vontade para consultar estes recursos:

11 Dicas de Configuração

O caminho mais fácil para se trabalhar com o Rails é armazenar todos os dados externos como UTF-8. Se não fizer assim, as bibliotecas Ruby e o Rails vão, na maioria das vezes, conseguir converter seus dados nativos em UTF-8, porém não é sempre que isso funciona corretamente, então é melhor que você assegure que todos seus dados externos estão em UTF-8.

Caso tenha cometido um erro nessa parte, o sintoma mais comum é o aparecimento de um diamante preto com um ponto de interrogação dentro no seu navegador. Outro sintoma comum é o aparecimento de caracteres como "ü" ao invés de "ü". O Rails executa um número de passos internos para mitigar causas comuns desses problemas que possam ser detectadas e corrigidas automaticamente. Porém, caso você possua dados externos que não estão armazenados como UTF-8, eles poderão ocasionalmente resultar em problemas que não podem ser detectados e nem resolvidos de forma automática pelo Rails.

Duas fontes muito comuns de dados que não estão em UTF-8 são:

  • Seu editor de texto: A maioria dos editores de texto (como o TextMate), salvam os arquivos em UTF-8 de forma padrão. Caso o seu editor de texto não salve, isso pode resultar em caracteres especiais inseridos por você nos seus templates (como por exemplo: é) aparecerem no navegador como um diamante com um ponto de interrogação dentro. Isso também se aplica aos seus arquivos de tradução i18n. Muitos editores que não salvam em UTF-8 por padrão (como algumas versões do Dreamweaver) oferecem uma forma de alterar o padrão para UTF-8. Faça isso.
  • Seu banco de dados: o Rails converte seus dados do banco de dados em UTF-8 de forma padrão. Porém, se seu banco de dados não está utilizando UTF-8 internamente, pode ser que não consiga armazenar todos os caracteres que seus usuários insiram. Por exemplo, se seu banco de dados está utilizando Latin-1 internamente, e seu usuário inserir um caractere russo, hebraico ou japonês, os dados serão perdidos para sempre assim que entrarem no banco de dados. Se possível, utilize UTF-8 como padrão de armazenamento para o seu banco de dados.

Feedback

Você é incentivado a ajudar a melhorar a qualidade deste guia.

Por favor, contribua se vir qualquer erros, inclusive erros de digitação. Para começar, você pode ler nossa sessão de contribuindo com a documentação.

Você também pode encontrar conteúdo incompleto ou coisas que não estão atualizadas. Por favor, adicione qualquer documentação em falta na master do Rails. Certifique-se de checar o Edge Guides (en-US) primeiro para verificar se o problema já foi resolvido ou não no branch master. Verifique as Diretrizes do Guia Ruby on Rails para estilo e convenções.

Se, por qualquer motivo, você encontrar algo para consertar, mas não conseguir consertá-lo, por favor abra uma issue no nosso Guia.

E por último, mas não menos importante, qualquer tipo de discussão sobre a documentação do Ruby on Rails é muito bem vinda na lista de discussão rubyonrails-docs e nas issues do Guia em português.