Como Criar Recursos Aninhados para uma Aplicação Ruby on Rails

Introdução

Ruby on Rails é um framework de aplicação web escrito em Ruby que oferece aos desenvolvedores uma abordagem opinativa para o desenvolvimento de aplicações. Trabalhar com Rails oferece aos desenvolvedores:

  • Convenções para lidar com coisas como roteamento, dados em estado, e gerenciamento de ativos.
  • A firm grounding in the model-view-controller (MCV) architectural pattern, which separates an application’s logic, located in models, from the presentation and routing of application information.

À medida que você adiciona complexidade às suas aplicações Rails, é provável que trabalhe com vários modelos, que representam a lógica de negócios da sua aplicação e interagem com o seu banco de dados. Adicionar modelos relacionados significa estabelecer relacionamentos significativos entre eles, o que afeta como as informações são transmitidas pelos controladores da sua aplicação e como são capturadas e apresentadas de volta aos usuários por meio das visualizações.

Neste tutorial, você irá construir em cima de uma aplicação Rails existente que oferece aos usuários fatos sobre tubarões. Esta aplicação já possui um modelo para lidar com dados de tubarões, mas você irá adicionar um recurso aninhado para postagens sobre tubarões individuais. Isso permitirá que os usuários desenvolvam uma ampla gama de pensamentos e opiniões sobre tubarões individuais.

Pré-requisitos

Para seguir este tutorial, você precisará:

  • A local machine or development server running Ubuntu 18.04. Your development machine should have a non-root user with administrative privileges and a firewall configured with ufw. For instructions on how to set this up, see our Initial Server Setup with Ubuntu 18.04 tutorial.
  • Node.js e npm instalados na sua máquina local ou servidor de desenvolvimento. Este tutorial utiliza a versão do Node.js 10.16.3 e a versão do npm 6.9.0. Para orientações sobre a instalação do Node.js e npm no Ubuntu 18.04, siga as instruções na seção “Instalando Usando um PPA” de Como Instalar o Node.js no Ubuntu 18.04.
  • Ruby, rbenv, e Rails instalados na sua máquina local ou servidor de desenvolvimento, seguindo os Passos 1-4 em Como Instalar o Ruby on Rails com rbenv no Ubuntu 18.04. Este tutorial utiliza Ruby 2.5.1, rbenv 1.1.2, e Rails 5.2.3.
  • SQLite instalado, e uma aplicação básica de informações sobre tubarões criada, seguindo as direções em Como Construir uma Aplicação Ruby on Rails.

Passo 1 — Criando o Modelo Aninhado

Nossa aplicação irá tirar proveito das associações do Active Record para construir um relacionamento entre os modelos Shark e Post: os posts pertencerão a tubarões específicos, e cada tubarão pode ter vários posts. Portanto, nossos modelos Shark e Post estarão relacionados através das associações belongs_to e has_many.

O primeiro passo para construir a aplicação desta maneira será criar um modelo Post e recursos relacionados. Para fazer isso, podemos usar o comando rails generate scaffold, que nos fornecerá um modelo, uma migração de banco de dados para alterar o esquema do banco de dados, um controlador, um conjunto completo de visualizações para gerenciar as operações padrão de Criar, Ler, Atualizar e Excluir (CRUD), e templates para partes, ajudantes e testes. Precisaremos modificar esses recursos, mas usar o comando scaffold nos economizará tempo e energia, pois ele gera uma estrutura que podemos usar como ponto de partida.

Primeiro, certifique-se de que você está no diretório sharkapp do projeto Rails que você criou nos pré-requisitos:

  1. cd sharkapp

Crie seus recursos Post com o seguinte comando:

rails generate scaffold Post body:text shark:references

Com body:text, estamos dizendo ao Rails para incluir um campo body na tabela de banco de dados posts — a tabela que mapeia para o modelo Post. Também estamos incluindo a palavra-chave :references, que configura uma associação entre os modelos Shark e Post. Especificamente, isso garantirá que uma chave estrangeira representando cada entrada de tubarão no banco de dados sharks seja adicionada ao banco de dados posts.

Depois de executar o comando, você verá uma saída confirmando os recursos que o Rails gerou para a aplicação. Antes de prosseguir, você pode verificar o arquivo de migração do banco de dados para ver o relacionamento que agora existe entre seus modelos e tabelas de banco de dados. Use o seguinte comando para ver o conteúdo do arquivo, certificando-se de substituir o carimbo de data/hora no seu próprio arquivo de migração pelo que está mostrado aqui:

  1. cat db/migrate/20190805132506_create_posts.rb

Você verá a seguinte saída:

Output
class CreatePosts < ActiveRecord::Migration[5.2] def change create_table :posts do |t| t.text :body t.references :shark, foreign_key: true t.timestamps end end end

Como você pode ver, a tabela inclui uma coluna para uma chave estrangeira de tubarão. Esta chave terá a forma de nome_do_modelo_id — no nosso caso, tubarão_id.

O Rails estabeleceu o relacionamento entre os modelos em outro lugar também. Dê uma olhada no modelo Post recém-gerado com o seguinte comando:

cat app/models/post.rb
Output
class Post < ApplicationRecord belongs_to :shark end

A associação belongs_to configura um relacionamento entre modelos em que uma única instância do modelo declarante pertence a uma única instância do modelo nomeado. No caso de nossa aplicação, isso significa que um único post pertence a um único tubarão.

Além de definir esse relacionamento, o comando rails generate scaffold também criou rotas e visualizações para as postagens, assim como fez para nossos recursos de tubarão no Passo 3 de Como Construir uma Aplicação Ruby on Rails.

Este é um bom começo, mas precisaremos configurar algumas rotas adicionais e consolidar a associação do Active Record para o modelo Shark para que o relacionamento entre nossos modelos e rotas funcione conforme desejado.

Passo 2 — Especificando Rotas Aninhadas e Associações para o Modelo Pai

O Rails já definiu a associação belongs_to em nosso modelo Post, graças à palavra-chave :references no comando rails generate scaffold, mas para que esse relacionamento funcione corretamente, precisaremos especificar uma associação has_many também em nosso modelo Shark. Também precisaremos fazer alterações na rota padrão que o Rails nos deu para tornar os recursos de postagem filhos dos recursos de tubarão.

Para adicionar a associação has_many ao modelo Shark, abra app/models/shark.rb usando nano ou seu editor favorito:

  1. nano app/models/shark.rb

Adicione a seguinte linha ao arquivo para estabelecer o relacionamento entre tubarões e posts:

~/sharkapp/app/models/shark.rb
class Shark < ApplicationRecord
  has_many :posts
  validates :name, presence: true, uniqueness: true
  validates :facts, presence: true
end

Uma coisa que vale a pena pensar aqui é o que acontece com os posts uma vez que um tubarão específico é deletado. Provavelmente, não queremos que os posts associados a um tubarão deletado persistam no banco de dados. Para garantir que quaisquer posts associados a um determinado tubarão sejam eliminados quando esse tubarão for deletado, podemos incluir a opção dependent com a associação.

Adicione o seguinte código ao arquivo para garantir que a ação destroy em um determinado tubarão delete quaisquer posts associados:

~/sharkapp/app/models/shark.rb
class Shark < ApplicationRecord
  has_many :posts , dependent: :destroy
  validates :name, presence: true, uniqueness: true
  validates :facts, presence: true
end

Depois de terminar de fazer essas alterações, salve e feche o arquivo. Se estiver usando nano, você pode fazer isso pressionando CTRL+X, Y, e então ENTER.

Em seguida, abra seu arquivo config/routes.rb para modificar o relacionamento entre suas rotas de recursos:

  1. nano config/routes.rb

Atualmente, o arquivo parece assim:

~/sharkapp/config/routes.rb
Rails.application.routes.draw do
  resources :posts 
  resources :sharks

  root 'sharks#index'
  # Para detalhes sobre a DSL disponível dentro deste arquivo, veja http://guides.rubyonrails.org/routing.html
end

O código atual estabelece um relacionamento independente entre nossas rotas, quando o que gostaríamos de expressar é um relacionamento dependente entre tubarões e seus posts associados.

Vamos atualizar nossa declaração de rota para fazer :sharks o pai de :posts. Atualize o código no arquivo para se parecer com o seguinte:

~/sharkapp/config/routes.rb
Rails.application.routes.draw do
  resources :sharks do
    resources :posts
end
  root 'sharks#index'
  # Para detalhes sobre a DSL disponível dentro deste arquivo, consulte http://guides.rubyonrails.org/routing.html
end

Salve e feche o arquivo quando terminar de editar.

Com essas mudanças feitas, você pode prosseguir para atualizar o seu controlador de posts.

Passo 3 — Atualizando o Controlador de Posts

A associação entre nossos modelos nos fornece métodos que podemos usar para criar novas instâncias de post associadas a determinados tubarões. Para usar esses métodos, precisaremos adicioná-los ao nosso controlador de posts.

Abra o arquivo do controlador de posts:

  1. nano app/controllers/posts_controller.rb

Atualmente, o arquivo se parece com isso:

~/sharkapp/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :set_post, only: [:show, :edit, :update, :destroy]

  # OBTER /posts
  # OBTER /posts.json
  def index
    @posts = Post.all
  end

  # OBTER /posts/1
  # OBTER /posts/1.json
  def show
  end

  # OBTER /posts/new
  def new
    @post = Post.new
  end

  # OBTER /posts/1/edit
  def edit
  end

  # POSTAR /posts
  # POSTAR /posts.json
  def create
    @post = Post.new(post_params)

    respond_to do |format|
      if @post.save
        format.html { redirect_to @post, notice: 'Post was successfully created.' }
        format.json { render :show, status: :created, location: @post }
      else
        format.html { render :new }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # ATUALIZAR/COLOCAR /posts/1
  # ATUALIZAR/COLOCAR /posts/1.json
  def update
    respond_to do |format|
      if @post.update(post_params)
        format.html { redirect_to @post, notice: 'Post was successfully updated.' }
        format.json { render :show, status: :ok, location: @post }
      else
        format.html { render :edit }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETAR /posts/1
  # DELETAR /posts/1.json
  def destroy
    @post.destroy
    respond_to do |format|
      format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks para compartilhar configuração comum ou restrições entre ações.
    def set_post
      @post = Post.find(params[:id])
    end

    # Nunca confie em parâmetros da assustadora internet, apenas permita a lista branca.
    def post_params
      params.require(:post).permit(:body, :shark_id)
    end
end

Como nosso controlador de tubarões, os métodos deste controlador trabalham com instâncias da classe associada Post. Por exemplo, o método novo cria uma nova instância da classe Post, o método índice captura todas as instâncias da classe, e o método definir_post usa find e params para selecionar um post específico pelo id. No entanto, se quisermos que nossas instâncias de post estejam associadas a instâncias específicas de tubarões, então precisaremos modificar esse código, já que a classe Post está atualmente operando como uma entidade independente.

Nossas modificações farão uso de duas coisas:

  • Os métodos que se tornaram disponíveis para nós quando adicionamos as associações belongs_to e has_many aos nossos modelos. Especificamente, agora temos acesso ao método build graças à associação has_many que definimos em nosso modelo Shark. Este método nos permitirá criar uma coleção de objetos de post associados a um objeto de tubarão específico, usando a chave estrangeira shark_id que existe em nosso banco de dados de posts.
  • As rotas e os ajudantes de roteamento que se tornaram disponíveis quando criamos uma rota posts aninhada. Para uma lista completa de rotas de exemplo que se tornam disponíveis quando você cria relacionamentos aninhados entre recursos, consulte a documentação do Rails. Por enquanto, será suficiente para nós saber que para cada tubarão específico — digamos sharks/1 — haverá uma rota associada para posts relacionados a esse tubarão: sharks/1/posts. Também haverá ajudantes de roteamento como shark_posts_path(@shark) e edit_sharks_posts_path(@shark) que se referem a essas rotas aninhadas.

No arquivo, começaremos escrevendo um método, get_shark, que será executado antes de cada ação no controlador. Este método criará uma variável de instância local @shark encontrando uma instância de tubarão por shark_id. Com esta variável disponível para nós no arquivo, será possível relacionar posts a um tubarão específico nos outros métodos.

Acima dos outros métodos private no final do arquivo, adicione o seguinte método:

~/sharkapp/controllers/posts_controller.rb
. . . 
private
  def get_shark
@shark = Shark.find(params[:shark_id])
end
  # Use callbacks to share common setup or constraints between actions.
. . . 

Em seguida, adicione o filtro correspondente ao topo do arquivo, antes do filtro existente:

~/sharkapp/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :get_shark

Isso garantirá que get_shark seja executado antes de cada ação definida no arquivo.

Em seguida, você pode usar esta instância @shark para reescrever o método index. Em vez de pegar todas as instâncias da classe Post, queremos que este método retorne todas as instâncias de post associadas a uma instância específica de shark.

Modifique o método index para ficar assim:

~/sharkapp/controllers/posts_controller.rb
. . .
  def index
    @posts = @shark.posts
  end
. . .

O método new precisará de uma revisão semelhante, já que queremos que uma nova instância de post seja associada a um shark específico. Para conseguir isso, podemos usar o método build, juntamente com nossa variável de instância local @shark.

Altere o método new para ficar assim:

~/sharkapp/controllers/posts_controller.rb
. . . 
  def new
    @post = @shark.posts.build
  end
. . . 

Este método cria um objeto de post associado à instância de shark específica do método get_shark.

Em seguida, abordaremos o método mais intimamente relacionado ao new: create. O método create faz duas coisas: ele cria uma nova instância de post usando os parâmetros que os usuários inseriram no formulário new, e, se não houver erros, ele salva essa instância e usa um auxiliar de rota para redirecionar os usuários para onde eles podem ver o novo post. No caso de erros, ele renderiza o modelo new novamente.

Atualize o método create para se parecer com isto:

~/sharkapp/controllers/posts_controller.rb
  def create
    @post = @shark.posts.build(post_params)

        respond_to do |format|
         if @post.save  
            format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully created.' }
            format.json { render :show, status: :created, location: @post }
         else
            format.html { render :new }
            format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

Em seguida, dê uma olhada no método update. Este método utiliza uma variável de instância @post, que não é explicitamente definida no próprio método. De onde vem essa variável?

Dê uma olhada nos filtros no topo do arquivo. O segundo filtro before_action auto-gerado fornece uma resposta:

~/sharkapp/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :get_shark
  before_action :set_post, only: [:show, :edit, :update, :destroy]
  . . .

O método update (assim como show, edit e destroy) recebe uma variável @post do método set_post. Esse método, listado sob o método get_shark com nossos outros métodos private, atualmente se parece com isto:

~/sharkapp/controllers/posts_controller.rb
. . . 
private
. . . 
  def set_post
    @post = Post.find(params[:id])
  end
. . .

Seguindo os métodos que utilizamos em outros lugares do arquivo, precisaremos modificar este método para que @post se refira a uma instância específica na coleção de postagens que está associada a um determinado tubarão. Mantenha em mente o método build aqui — graças às associações entre nossos modelos, e aos métodos (como build) que estão disponíveis para nós em virtude dessas associações, cada uma de nossas instâncias de postagem faz parte de uma coleção de objetos que está associada a um tubarão específico. Portanto, faz sentido que ao consultar uma postagem específica, consultemos a coleção de postagens associadas a um tubarão específico.

Atualize o set_post para se parecer com isto:

~/sharkapp/controllers/posts_controller.rb
. . . 
private
. . . 
  def set_post
    @post = @shark.posts.find(params[:id])
  end
. . .

Em vez de encontrar uma instância particular de toda a classe Post pelo id, nós procuramos por um id correspondente na coleção de postagens associadas a um tubarão específico.

Com esse método atualizado, podemos analisar os métodos update e destroy.

O método update utiliza a variável de instância @post de set_post e a utiliza com os post_params que o usuário inseriu no formulário edit. Em caso de sucesso, queremos que o Rails redirecione o usuário de volta para a visualização index das postagens associadas a um determinado tubarão. Em caso de erros, o Rails irá renderizar novamente o modelo edit.

Neste caso, a única alteração que precisaremos fazer é no comando redirect_to, para lidar com atualizações bem-sucedidas. Atualize-o para redirecionar para shark_post_path(@shark), que redirecionará para a visualização index das postagens do tubarão selecionado:

~/sharkapp/controllers/posts_controller.rb
. . . 
  def update
    respond_to do |format|
      if @post.update(post_params)
        format.html { redirect_to shark_post_path(@shark), notice: 'Post was successfully updated.' }
        format.json { render :show, status: :ok, location: @post }
      else
        format.html { render :edit }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end
. . .

Em seguida, faremos uma alteração semelhante no método destroy. Atualize o método redirect_to para redirecionar as solicitações para shark_posts_path(@shark) em caso de sucesso:

~/sharkapp/controllers/posts_controller.rb
. . . 
  def destroy
    @post.destroy
     respond_to do |format|
      format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully destroyed.' }
      format.json { head :no_content }
    end
  end
. . .

Esta é a última alteração que faremos. Agora você possui um arquivo de controlador de postagens que se parece com isto:

~/sharkapp/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :get_shark
  before_action :set_post, only: [:show, :edit, :update, :destroy]

  # OBTÉM /posts
  # OBTÉM /posts.json
  def index
    @posts = @shark.posts
  end

  # OBTÉM /posts/1
  # OBTÉM /posts/1.json
  def show
  end

  # OBTÉM /posts/new
  def new
    @post = @shark.posts.build
  end

  # OBTÉM /posts/1/edit
  def edit
  end

  # ENVIA /posts
  # ENVIA /posts.json
  def create
    @post = @shark.posts.build(post_params)

        respond_to do |format|
         if @post.save  
            format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully created.' }
            format.json { render :show, status: :created, location: @post }
         else
            format.html { render :new }
            format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /posts/1
  # PATCH/PUT /posts/1.json
  def update
    respond_to do |format|
      if @post.update(post_params)
        format.html { redirect_to shark_post_path(@shark), notice: 'Post was successfully updated.' }
        format.json { render :show, status: :ok, location: @post }
      else
        format.html { render :edit }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # EXCLUI /posts/1
  # EXCLUI /posts/1.json
  def destroy
    @post.destroy
    respond_to do |format|
      format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private

   def get_shark
     @shark = Shark.find(params[:shark_id])
   end
    # Use callbacks to share common setup or constraints between actions.
    def set_post
      @post = @shark.posts.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def post_params
      params.require(:post).permit(:body, :shark_id)
    end
end

O controlador gerencia como a informação é passada dos modelos de visualização para o banco de dados e vice-versa. Nosso controlador agora reflete a relação entre nossos modelos de Shark e Post, nos quais posts estão associados a tubarões específicos. Podemos prosseguir para a modificação dos próprios modelos de visualização, onde os usuários irão inserir e modificar informações sobre postagens de tubarões específicos.

Passo 4 — Modificando as Visualizações

Nossas revisões nos modelos de visualização envolverão alterar os modelos que se relacionam com as postagens e também modificar nossa visualização de show de tubarões, já que queremos que os usuários vejam as postagens associadas a tubarões específicos.

Vamos começar com o modelo fundamental para nossas postagens: o parcial form que é reutilizado em vários modelos de postagem. Abra esse formulário agora:

  1. nano app/views/posts/_form.html.erb

Em vez de passar apenas o modelo post para o ajudante de formulário form_with, vamos passar ambos os modelos shark e post, com post definido como um recurso filho.

Altere a primeira linha do arquivo para que fique assim, refletindo a relação entre nossos recursos shark e post:

~/sharkapp/views/posts/_form.html.erb
<%= form_with(model: [@shark, post], local: true) do |form| %>
. . . 

Em seguida, exclua a seção que lista o shark_id do tubarão relacionado, pois esta não é uma informação essencial na visualização.

O formulário concluído, completo com nossas edições na primeira linha e sem a seção excluída shark_id, ficará assim:

~/sharkapp/views/posts/_form.html.erb
<%= form_with(model: [@shark, post], local: true) do |form| %>
  <% if post.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:</h2>

      <ul>
      <% post.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= form.label :body %>
    <%= form.text_area :body %>
  </div>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

Salve e feche o arquivo quando terminar de editar.

Em seguida, abra a visualização index, que mostrará as postagens associadas a um tubarão específico:

  1. nano app/views/posts/index.html.erb

Graças ao comando rails generate scaffold, o Rails gerou a maior parte do modelo, completo com uma tabela que mostra o campo body de cada postagem e seu shark associado.

Assim como o outro código que já modificamos, no entanto, este modelo trata as postagens como entidades independentes, quando gostaríamos de utilizar as associações entre nossos modelos e as coleções e métodos auxiliares que essas associações nos proporcionam.

No corpo da tabela, faça as seguintes atualizações:

Primeiro, atualize post.shark para post.shark.name, para que a tabela inclua o campo de nome do tubarão associado, em vez de informações de identificação sobre o próprio objeto tubarão.

~/sharkapp/app/views/posts/index.html.erb
. . . 
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
. . . 

Em seguida, altere o redirecionamento Show para direcionar os usuários para a visualização show do tubarão associado, já que eles provavelmente vão querer uma maneira de navegar de volta para o tubarão original. Podemos fazer uso da variável de instância @shark que definimos no controlador aqui, já que o Rails torna as variáveis de instância criadas no controlador disponíveis para todas as visualizações. Também vamos mudar o texto do link de Show para Show Shark, para que os usuários entendam melhor sua função.

Atualize esta linha para o seguinte:

~/sharkapp/app/views/posts/index.html.erb
. . . 
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
        <td><%= link_to 'Show Shark', [@shark] %></td>

Na próxima linha, queremos garantir que os usuários sejam roteados para o caminho aninhado correto quando forem editar um post. Isso significa que, em vez de serem direcionados para posts/post_id/edit, os usuários serão direcionados para sharks/shark_id/posts/post_id/edit. Para fazer isso, vamos usar o auxiliar de roteamento shark_post_path e nossos modelos, que o Rails tratará como URLs. Também vamos atualizar o texto do link para tornar sua função mais clara.

Atualize a linha Edit para ficar assim:

~/sharkapp/app/views/posts/index.html.erb
. . . 
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
        <td><%= link_to 'Show Shark', [@shark] %></td>
        <td><%= link_to 'Edit Post', edit_shark_post_path(@shark, post) %></td>

Em seguida, vamos fazer uma alteração semelhante no link Destroy, atualizando sua função na string e adicionando nossos recursos shark e post:

~/sharkapp/app/views/posts/index.html.erb
. . . 
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
        <td><%= link_to 'Show Shark', [@shark] %></td>
        <td><%= link_to 'Edit Post', edit_shark_post_path(@shark, post) %></td>
        <td><%= link_to 'Destroy Post', [@shark, post], method: :delete, data: { confirm: 'Are you sure?' } %></td>

Por fim, no final do formulário, vamos querer atualizar o caminho New Post para levar os usuários ao caminho aninhado apropriado quando desejarem criar um novo post. Atualize a última linha do arquivo para usar o auxiliar de roteamento new_shark_post_path(@shark):

~/sharkapp/app/views/posts/index.html.erb
. . . 
<%= link_to 'New Post', new_shark_post_path(@shark) %>

O arquivo final terá esta aparência:

~/sharkapp/app/views/posts/index.html.erb
<p id="notice"><%= notice %></p>

<h1>Posts</h1>

<table>
  <thead>
    <tr>
      <th>Body</th>
      <th>Shark</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
        <td><%= link_to 'Show Shark', [@shark] %></td>
        <td><%= link_to 'Edit Post', edit_shark_post_path(@shark, post) %></td>
        <td><%= link_to 'Destroy Post', [@shark, post], method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Post', new_shark_post_path(@shark) %>

Salvar e fechar o arquivo quando terminar de editar.

As outras edições que faremos nas visualizações de postagens não serão tão numerosas, já que nossas outras visualizações usam o form parcial que já editamos. No entanto, vamos querer atualizar as referências link_to nos outros modelos de postagens para refletir as alterações que fizemos em nosso parcial form.

Abra app/views/posts/new.html.erb:

  1. nano app/views/posts/new.html.erb

Atualize a referência link_to no final do arquivo para fazer uso do auxiliar shark_posts_path(@shark):

~/sharkapp/app/views/posts/new.html.erb
. . . 
<%= link_to 'Back', shark_posts_path(@shark) %>

Salvar e fechar o arquivo quando terminar de fazer essa alteração.

Em seguida, abra o modelo edit:

  1. nano app/views/posts/edit.html.erb

Além do caminho Back, vamos atualizar Show para refletir nossos recursos aninhados. Altere as duas últimas linhas do arquivo para ficarem assim:

~/sharkapp/app/views/posts/edit.html.erb
. . . 
<%= link_to 'Show', [@shark, @post] %> |
<%= link_to 'Back', shark_posts_path(@shark) %>

Salvar e fechar o arquivo.

Em seguida, abra o modelo show:

nano app/views/posts/show.html.erb

Faça as seguintes edições nos caminhos Edit e Back no final do arquivo:

~/sharkapp/app/views/posts/edit.html.erb
. . .
<%= link_to 'Edit', edit_shark_post_path(@shark, @post) %> |
<%= link_to 'Back', shark_posts_path(@shark) %>

Salvar e fechar o arquivo quando terminar.

Como último passo, vamos querer atualizar a visualização show para nossos tubarões para que as postagens sejam visíveis para tubarões individuais. Abra esse arquivo agora:

  1. nano app/views/sharks/show.html.erb

Nossas edições aqui incluirão adicionar uma seção Posts ao formulário e um link Add Post no final do arquivo.

Abaixo dos Fatos para um determinado tubarão, vamos adicionar uma nova seção que itera através de cada instância na coleção de posts associados a este tubarão, produzindo o corpo de cada postagem.

Adicione o seguinte código abaixo da seção Fatos do formulário e acima dos redirecionamentos no final do arquivo:

~/sharkapp/app/views/sharks/show.html.erb
. . .
<p>
  <strong>Facts:</strong>
  <%= @shark.facts %>
</p>

<h2>Posts</h2>
<% for post in @shark.posts %>
    <ul>
      <li><%= post.body %></li>
  </ul>
<% end %>

<%= link_to 'Edit', edit_shark_path(@shark) %> |
. . . 

Em seguida, adicione um novo redirecionamento para permitir que os usuários adicionem uma nova postagem para este tubarão específico:

~/sharkapp/app/views/sharks/show.html.erb
. . .
<%= link_to 'Edit', edit_shark_path(@shark) %> |
<%= link_to 'Add Post', shark_posts_path(@shark) %> |
<%= link_to 'Back', sharks_path %>

Salve e feche o arquivo quando terminar de editar.

Agora você fez alterações nos modelos, controladores e visualizações de sua aplicação para garantir que as postagens estejam sempre associadas a um tubarão específico. Como etapa final, podemos adicionar algumas validações ao nosso modelo Post para garantir consistência nos dados salvos no banco de dados.

Passo 5 — Adicionando Validações e Testando a Aplicação

No Passo 5 do Como Construir uma Aplicação Ruby on Rails, você adicionou validações ao seu modelo Shark para garantir uniformidade e consistência nos dados que são salvos no banco de dados sharks. Agora, vamos dar um passo semelhante para garantir garantias para o banco de dados posts também.

Abra o arquivo onde seu modelo Post é definido:

  1. nano app/models/post.rb

Aqui, queremos garantir que as postagens não estejam em branco e que elas não contenham conteúdo duplicado que outros usuários possam ter postado. Para conseguir isso, adicione a seguinte linha ao arquivo:

~/sharkapp/app/models/post.rb
class Post < ApplicationRecord
  belongs_to :shark
  validates :body, presence: true, uniqueness: true
end

Salve e feche o arquivo quando terminar de editar.

Com essa última alteração feita, você está pronto para executar suas migrações e testar a aplicação.

Primeiro, execute suas migrações:

  1. rails db:migrate

Em seguida, inicie seu servidor. Se estiver trabalhando localmente, você pode fazer isso executando:

  1. rails s

Se estiver trabalhando em um servidor de desenvolvimento, execute o seguinte comando em vez disso:

  1. rails s --binding=your_server_ip

Navegue até a raiz da sua aplicação em http://localhost:3000 ou http://seu_ip_do_servidor:3000.

O tutorial do projeto Rails pré-requisito guiou você através da adição e edição de uma entrada de tubarão Grande Branco. Se você não adicionou mais tubarões, a página inicial da aplicação terá este aspecto:

Clique em Mostrar ao lado do nome do Great White. Isso o levará à visualização mostrar para este tubarão. Você verá o nome do tubarão e seus fatos, e um cabeçalho Postagens sem conteúdo. Vamos adicionar uma postagem para preencher esta parte do formulário.

Clique em Adicionar Postagem abaixo do cabeçalho Postagens. Isso o levará à visualização de índice de postagem, onde você terá a chance de selecionar Nova Postagem:

Graças aos mecanismos de autenticação que você implementou no Passo 6 do Como Construir uma Aplicação Ruby on Rails, você pode ser solicitado a autenticar com o nome de usuário e senha que você criou naquele Passo, dependendo se você criou ou não uma nova sessão.

Clique em Nova Postagem, que o levará ao seu modelo novo de postagem:

No campo Corpo, digite “Esses tubarões são assustadores!”

Clique em Criar Postagem. Você será redirecionado para a visualização índice de todas as postagens que pertencem a este tubarão:

Com nossos recursos de postagem funcionando, agora podemos testar nossas validações de dados para garantir que apenas os dados desejados sejam salvos no banco de dados.

Na visualização índice, clique em Nova Postagem. No campo Corpo do novo formulário, tente inserir “Esses tubarões são assustadores!” novamente:

Clique em Criar Postagem. Você verá o seguinte erro:

Clique em Voltar para retornar à página principal das postagens.

Para testar nossa outra validação, clique em Nova Postagem novamente. Deixe a postagem em branco e clique em Criar Postagem. Você verá o seguinte erro:

Com seus recursos aninhados e validações funcionando corretamente, agora você tem um aplicativo Rails funcionando que pode ser usado como ponto de partida para desenvolvimentos futuros.

Conclusão

Com seu aplicativo Rails no lugar, agora você pode trabalhar em coisas como estilização e desenvolvimento de outros componentes front-end. Se você gostaria de aprender mais sobre roteamento e recursos aninhados, a documentação do Rails é um ótimo lugar para começar.

Para saber mais sobre a integração de frameworks front-end com seu aplicativo, dê uma olhada em Como Configurar um Projeto Ruby on Rails com um Frontend React.

Source:
https://www.digitalocean.com/community/tutorials/how-to-create-nested-resources-for-a-ruby-on-rails-application