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 aplicativos. Trabalhar com Rails proporciona aos desenvolvedores:

  • Convenções para lidar com coisas como roteamento, dados em estado de sessão 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.

Ao adicionar complexidade às suas aplicações Rails, é provável que você 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 então afeta como as informações são transmitidas pelos controladores da sua aplicação e como são capturadas e apresentadas aos usuários por meio das visualizações.

Neste tutorial, você vai 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ê adicionará um recurso aninhado para postagens sobre tubarões individuais. Isso permitirá que os usuários expandam um corpo maior 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.
  • O Node.js e o npm instalados em sua máquina local ou servidor de desenvolvimento. Este tutorial usa a versão 10.16.3 do Node.js e a versão 6.9.0 do npm. Para orientações sobre como instalar o Node.js e o 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.
  • O Ruby, o rbenv, e o Rails instalados em 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 usa o Ruby 2.5.1, rbenv 1.1.2, e Rails 5.2.3.
  • O SQLite instalado, e uma aplicação básica de informações sobre tubarões criada, seguindo as instruções em Como Construir uma Aplicação Ruby on Rails.

Passo 1 — Criando o Modelo Aninhado

A nossa aplicação aproveitará as associações do Active Record para estabelecer uma relação entre os modelos Shark e Post: os posts pertencerão a tubarões específicos e cada tubarão poderá ter vários posts. Portanto, os modelos Shark e Post estarão relacionados através das associações belongs_to e has_many.

O primeiro passo para desenvolver a aplicação desta maneira será criar um modelo Post e os 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 operações padrão de Criação, Leitura, Atualização e Exclusão (CRUD) e modelos para partials, helpers e testes. Teremos que modificar esses recursos, mas o uso do comando scaffold economizará algum tempo e energia, já que gera uma estrutura que podemos usar como ponto de partida.

Primeiro, certifique-se de que você está na pasta sharkapp para o projeto Rails que você criou nos pré-requisitos:Crie seus recursos Post com o seguinte comando:

  1. cd sharkapp

Crie seus recursos Post com o seguinte comando:

rails generate scaffold Post body:text shark:references

Com body:text, estamos informando ao Rails para incluir um campo body na tabela posts do banco de dados – a tabela que se relaciona ao 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 na tabela sharks seja adicionada à tabela posts do banco de dados.

Depois de executar o comando, você verá a saída confirmando os recursos que o Rails gerou para o aplicativo. Antes de continuar, você pode verificar o arquivo de migração do banco de dados para analisar a relação que agora existe entre seus modelos e tabelas do banco de dados. Use o seguinte comando para verificar o conteúdo do arquivo, substituindo o carimbo de data e hora do seu próprio arquivo de migração pelo 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. Essa chave assumirá a forma de model_name_id – em nosso caso, shark_id.

O Rails estabeleceu a relação entre os modelos em outro lugar também. Dê uma olhada no novo modelo Post gerado com o seguinte comando:

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

O belongs_to associação estabelece uma relação entre modelos em que uma única instância do modelo declarado pertence a uma única instância do modelo nomeado. No caso do nosso aplicativo, isso significa que um único post pertence a um único tubarão.

Além de estabelecer essa relação, o comando rails generate scaffold também criou rotas e visualizações para posts, assim como fez para nossos recursos de tubarão em Etapa3 de Como Construir um Aplicativo 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 a relação entre nossos modelos e rotas funcione como desejado.

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

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

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

  1. nano app/models/shark.rb

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

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

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

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, depois ENTER.

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

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

Atualmente, o arquivo se parece com isto:

  1. nano config/routes.rb

# Para detalhes sobre a DSL disponível dentro deste arquivo, consulte http://guides.rubyonrails.org/routing.htmlO 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.

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

  root 'sharks#index'
  # Para obter detalhes sobre o DSL disponível neste arquivo, consulte http://guides.rubyonrails.org/routing.html
end

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

Vamos atualizar nossa declaração de rota para tornar :sharks o pai de :posts. Atualize o código no arquivo para ficar da seguinte forma:

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

Salve e feche o arquivo quando terminar de editar.

Com essas alterações em vigor, você pode passar para a atualização do seu controlador posts.

Etapa3— Atualizando o Posts Controller

A associação entre nossos modelos nos fornece métodos que podemos usar para criar instâncias de posts novos associados a tubarões específicos. 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 isto:

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

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

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

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

  # GET /posts/1/edit
  def edit
  end

  # POST /posts
  # POST /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

  # PATCH/PUT /posts/1
  # PATCH/PUT /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

  # DELETE /posts/1
  # DELETE /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 to share common setup or constraints between actions.
    def set_post
      @post = Post.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

Como no nosso controlador de tubarões, os métodos deste controlador trabalham com instâncias da classe associada Post. Por exemplo, o método new cria uma nova instância da classe Post, o método index pega todas as instâncias da classe e o método set_post usa find e params para selecionar uma postagem específica por id. No entanto, se quisermos que nossas instâncias de postagem estejam associadas a instâncias de tubarão específicas, então precisaremos modificar este código, já que a classe Post está atualmente operando como uma entidade independente.

Nossas modificações usarão 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 determinado objeto de tubarão, usando a chave estrangeira shark_id que existe em nosso banco de dados de posts.
  • As rotas e ajudantes de roteamento que se tornaram disponíveis quando criamos uma rota aninhada posts. Para obter 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 postagens relacionadas 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 postagens a um tubarão específico nos outros métodos.

No arquivo, começaremos escrevendo um método, get_shark, que será executado antes de cada ação no controlador. Esse método criará uma variável de instância local @shark encontrando uma instância de tubarão por shark_id. Com essa variável disponível para nós no arquivo, será possível relacionar postagens a um tubarão específico nas outras 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 para compartilhar configurações comuns ou restrições entre ações. 
. . . 

Em seguida, adicione o filtro correspondente no 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 essa instância @shark para reescrever o método index. Em vez de pegar todas as instâncias da classe Post, queremos que esse método retorne todas as instâncias de postagens associadas a uma instância de tubarão específica.

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 postagem seja associada a um tubarão específico. Para conseguir isso, podemos usar o método build, junto 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
. . . 

Esse método cria um objeto de postagem que está associado à instância de tubarão específica da método get_shark.

Atualize o método create para ficar assim:

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

~/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

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

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 isso:

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

Para manter os métodos que já utilizamos em outras partes do arquivo, precisaremos modificar este método para que @post se refira a uma instância específica na coleção de posts associados a um tubarão específico. Mantenha o método build em mente aqui – graças às associações entre nossos modelos, e os métodos (como build) que estão disponíveis para nós em virtude dessas associações, cada uma de nossas instâncias de post faz parte de uma coleção de objetos que está associada a um tubarão específico. Portanto, faz sentido que ao consultar um post específico, consultemos a coleção de posts associada a um tubarão específico.Atualize set_post para ficar assim:Em vez de encontrar uma instância específica de toda a classe Post por id, procuramos por um id correspondente na coleção de posts associados a um tubarão específico.

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

Em conformidade com os métodos que utilizamos em outras partes do arquivo, precisaremos modificar esse método para que @post se refira a uma instância específica na coleção de postagens que está associada a um tubarão específico. Tenha em mente o método build aqui – graças às associações entre nossos modelos e os 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 associada a um tubarão específico.

Atualize set_post para ficar assim:

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

Em vez de encontrar uma instância específica de toda a classe Post pelo id, procuramos um id correspondente na coleção de postagens associada a um tubarão específico.

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

O método update faz uso da variável de instância @post de set_post e a usa com os post_params que o usuário inseriu no formulário edit. No caso de sucesso, queremos que o Rails envie o usuário de volta à visualização index das postagens associadas a um tubarão específico. No caso de erros, o Rails renderizará o modelo edit novamente.

Neste caso, a única alteração que precisaremos fazer é na instrução redirect_to, para lidar com atualizações bem-sucedidas. Atualize-a 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ê tem 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]

  # GET /posts
  # GET /posts.json
  def index
    @posts = @shark.posts
  end

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

  # GET /posts/new
  def new
    @post = @shark.posts.build
  end

  # GET /posts/1/edit
  def edit
  end

  # POST /posts
  # POST /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

  # DELETE /posts/1
  # DELETE /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 para compartilhar configurações comuns ou restrições entre ações.
    def set_post
      @post = @shark.posts.find(params[:id])
    end

    # Nunca confie em parâmetros provenientes da internet assustadora, permita apenas a lista branca.
    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 Shark e Post, em que as postagens estão associadas a tubarões específicos. Podemos passar para a modificação dos próprios modelos de visualização, que são onde os usuários inserirão e modificarão informações de postagens sobre tubarões específicos.

Passo 4 — Modificando Visualizações

Nossas revisões de modelo de visualização envolverão a alteração dos modelos relacionados a postagens e também a modificação de nossa visualização 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 postagens. Abra esse formulário agora:

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

Em vez de passar apenas o modelo post para o auxiliar de formulário form_with, passaremos tanto o modelo shark quanto o modelo post, com post definido como recurso filho.

Altere a primeira linha do arquivo para ficar assim, refletindo a relação entre nossos recursos tubarão e postagem:

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

Em seguida, apague a seção que lista o shark_id do tubarão relacionado, já que esta não é informação essencial na visualização.

O formulário concluído, completo com nossas edições na primeira linha e sem a seção shark_id deletada, 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

Em seguida, altere o redirecionamento do Show para direcionar os usuários para a visualização do show associado ao tubarão, pois 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 alterar 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:

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 atualizaremos o texto do link para tornar sua função mais clara.

Atualize a linha do Edit para se parecer com o seguinte:

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

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

Finalmente, na parte inferior do formulário, vamos atualizar o caminho do New Post para levar os usuários ao caminho aninhado apropriado quando quiserem criar um novo post. Atualize a última linha do arquivo para fazer uso do auxiliar de roteamento new_shark_post_path(@shark):O arquivo final 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>

Salve e feche 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 parcial form que já editamos. No entanto, queremos atualizar as referências link_to nos outros modelos de postagem para refletir as mudanças que fizemos em nosso parcial form.

~/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>

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

~/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>

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

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

Salve e feche o arquivo quando terminar de fazer essa alteração.

~/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) %>

Em seguida, abra o modelo edit:

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

Salve e feche o arquivo.

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

Em seguida, abra o modelo show:Faça as seguintes edições nos caminhos Edit e Back no final do arquivo:Salve e feche o arquivo quando terminar.Como último passo, vamos atualizar a visualização show para nossos tubarões para que as postagens sejam visíveis para tubarões individuais. Abra esse arquivo agora:Nossas edições aqui incluirão adicionar uma seção Posts ao formulário e um link Add Post no final do arquivo.

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

Salve e feche 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, atualizaremos o Show para refletir nossos recursos aninhados. Altere as duas últimas linhas do arquivo para ficar assim:

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

Salve e feche 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) %>

Salve e feche o arquivo quando terminar.

Como um passo final, queremos atualizar a visualização show para nossos tubarões para que as postagens sejam visíveis para tubarões individuais. Abra o 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.

Logo abaixo das Facts de um determinado tubarão, adicionaremos uma nova seção que itera por cada instância na coleção de postagens associadas a este tubarão, exibindo o body de cada postagem.

Adicione o seguinte código abaixo da seção Facts 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.

Você acabou de fazer alterações em seus modelos, controladores e views do aplicativo para garantir que os posts estejam sempre associados a um tubarão específico. Como um passo final, podemos adicionar algumas validações ao nosso modelo Post para garantir a consistência nos dados que são salvos no banco de dados.

Etapa 5 – Adicionando Validações e Testando o Aplicativo

Na Etapa 5 de Como Construir um Aplicativo Ruby on Rails, você adicionou validações ao seu modelo Shark para garantir a uniformidade e consistência nos dados que são salvos no banco de dados sharks. Agora, daremos 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 os posts não estejam em branco e que eles não dupliquem o conteúdo 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 em vigor, você está pronto para executar suas migrações e testar o aplicativo.

Primeiro, execute suas migrações:

  1. rails db:migrate

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

  1. rails s

Se você 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 do seu aplicativo em http://localhost:3000 ou http://sua_ip_do_servidor:3000.

O tutorial prévio do projeto Rails guiou você pela adição e edição de uma entrada de Tubarão Branco. Se você não adicionou mais tubarões, a página inicial do aplicativo ficará assim:

Clique em Mostrar ao lado do nome do Tubarão Branco. Isso o levará à visualização show para esse tubarão. Você verá o nome do tubarão e seus fatos, e um cabeçalho Posts sem conteúdo. Vamos adicionar um post para preencher essa parte do formulário.

Clique em Adicionar Post abaixo do cabeçalho Posts. Isso o levará à visualização index do post, onde você terá a chance de selecionar Novo Post:

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

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

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 esse 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.

A partir da 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 de 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 funcional que pode ser usado como ponto de partida para o desenvolvimento adicional.

Conclusão

Com sua aplicação Rails em funcionamento, agora você pode trabalhar em coisas como estilização e desenvolvimento de outros componentes de 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 aprender mais sobre a integração de frameworks de front-end com sua aplicação, 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