1
votes

I am working on an assignment for a project I have been working on. I was just introduced to refactoring my nested routes. Here are the changes.

routes.rb

   resources :topics do 
     resources :posts, except: [:index] do
       resources :comments, only: [:create, :destroy]
     end
   end

to

  resources :topics do
    resources :posts, except: [:index]
  end

  resources :posts, only: [] do
    resources :comments, only: [:create, :destroy]
  end

After this my instructions are as follows:

Run rake routes to see how this changes routing. Then make the following changes to refactor with these new, shallower routes:

Change the comment paths, in both the comments/_comment.html.erb and the comments/_form.html.erb partials.

Change the CommentsController actions so that they no longer initialize @topic.

Derive @topic from @post because we still want redirect_to the @post page after creating or destroying a comment. Furthermore, @post is still nested under @topic.

Visit posts, then delete and create comments to test this new shallow nesting.

rake routes

▶ rake routes
                  Prefix Verb   URI Pattern                                Controller#Action
         comments_create GET    /comments/create(.:format)                 comments#create
        new_user_session GET    /users/sign_in(.:format)                   devise/sessions#new
            user_session POST   /users/sign_in(.:format)                   devise/sessions#create
    destroy_user_session DELETE /users/sign_out(.:format)                  devise/sessions#destroy
           user_password POST   /users/password(.:format)                  devise/passwords#create
       new_user_password GET    /users/password/new(.:format)              devise/passwords#new
      edit_user_password GET    /users/password/edit(.:format)             devise/passwords#edit
                         PATCH  /users/password(.:format)                  devise/passwords#update
                         PUT    /users/password(.:format)                  devise/passwords#update
cancel_user_registration GET    /users/cancel(.:format)                    devise/registrations#cancel
       user_registration POST   /users(.:format)                           devise/registrations#create
   new_user_registration GET    /users/sign_up(.:format)                   devise/registrations#new
  edit_user_registration GET    /users/edit(.:format)                      devise/registrations#edit
                         PATCH  /users(.:format)                           devise/registrations#update
                         PUT    /users(.:format)                           devise/registrations#update
                         DELETE /users(.:format)                           devise/registrations#destroy
       user_confirmation POST   /users/confirmation(.:format)              devise/confirmations#create
   new_user_confirmation GET    /users/confirmation/new(.:format)          devise/confirmations#new
                         GET    /users/confirmation(.:format)              devise/confirmations#show
                    user PATCH  /users/:id(.:format)                       users#update
                         PUT    /users/:id(.:format)                       users#update
             topic_posts POST   /topics/:topic_id/posts(.:format)          posts#create
          new_topic_post GET    /topics/:topic_id/posts/new(.:format)      posts#new
         edit_topic_post GET    /topics/:topic_id/posts/:id/edit(.:format) posts#edit
              topic_post GET    /topics/:topic_id/posts/:id(.:format)      posts#show
                         PATCH  /topics/:topic_id/posts/:id(.:format)      posts#update
                         PUT    /topics/:topic_id/posts/:id(.:format)      posts#update
                         DELETE /topics/:topic_id/posts/:id(.:format)      posts#destroy
                  topics GET    /topics(.:format)                          topics#index
                         POST   /topics(.:format)                          topics#create
               new_topic GET    /topics/new(.:format)                      topics#new
              edit_topic GET    /topics/:id/edit(.:format)                 topics#edit
                   topic GET    /topics/:id(.:format)                      topics#show
                         PATCH  /topics/:id(.:format)                      topics#update
                         PUT    /topics/:id(.:format)                      topics#update
                         DELETE /topics/:id(.:format)                      topics#destroy
           post_comments POST   /posts/:post_id/comments(.:format)         comments#create
            post_comment DELETE /posts/:post_id/comments/:id(.:format)     comments#destroy
                   about GET    /about(.:format)                           welcome#about
                    root GET    /                                          welcome#index

_comment.html.erb

<% @comments.each do |comment| %>

<div class="media">
  <div class="media-left">
    <%= image_tag(comment.user.avatar.small.url, class: "media-object") if comment.user.try(:avatar) %>
  </div>
  <div class="media-body">
    <small>
      <% comment.user.name %> commented <%= time_ago_in_words(comment.created_at) %> ago
      <% if policy(comment).destroy? %>
        | <%= link_to "Delete", [@topic, @post, comment], method: :delete %>
      <% end %>
    </small>
    <p><%= comment.body %>
  </div>
</div>
<% end %>

I specifically changed line 11 to:

| <%= link_to "Delete", [@post, comment], method: :delete %>

_form.html.erb

<%= form_for [topic, post, comment] do |f| %>
  <% if comment.errors.any? %>
    <div class="alert alert-danger">
      <h4>There are <%= pluralize(comment.errors.count, "error") %>.</h4>
      <ul>
        <% comment.errors.full_messages.each do |msg| %>
          <li><%= msg %></li>
        <% end %>
      </ul>
    </div>
    <% end %>

    <div class="form-inline">
    <%= form_group_tag(comment.errors[:body]) do %>
      <%= f.label :body %>
      <%= f.text_field :body, class: 'form-control'%>
    <% end %>

    <div class="form-group">
      <%= f.submit "Comment", class: 'btn btn-default' %>
    </div>
  </div>
  <% end %>

The first line I have changed to:

<%= form_for [post, comment] do |f| %>

Now I am able to view the project when I start my Rails server. I am not sure how I will go about this last step.

Derive @topic from @post because we still want redirect_to the @post page after creating or destroying a comment. Furthermore, @post is still nested under @topic.

Here is my comments_controller.rb

class CommentsController < ApplicationController
  def create
    # find topic by id
    # @topic = Topic.find(params[:topic_id])
    # find post id through topic
    @post = Post.find(params[:post_id])
    # comments on post
    @comments = @post.comments

    @comment = current_user.comments.build(params.require(:comment).permit(:body, :post_id))
    @comment.post = @post


    authorize @comment
    if @comment.save
      flash[:notice] = "Comment was created."
      redirect_to [@topic, @post]
    else
      flash[:error] = "Error saving the comment. Please try again."
      # must render the the page calling the form!!
      render 'posts/show'
    end
  end

  def new

  end

  def destroy
    @topic = Topic.find(params[:topic_id])
    @post = @topic.posts.find(params[:post_id])
    @comment = @post.comments.find(params[:id])

    authorize @comment
    if @comment.destroy
      flash[:notice] = "Comment was removed."
      redirect_to [@topic, @post]
    else
      flash[:error] = "Comment couldn't be deleted. Try again."
      redirect_to [@topic, @post]
    end
  end
end

As you can see my route for showing posts is

topic_post GET    /topics/:topic_id/posts/:id(.:format)      posts#show

How am I supposed to do this when I am told to remove the @topic initialization? I have attempted to include it with my current modified code but I receive this error when attempting to delete a comment.

Processing by CommentsController#destroy as HTML
  Parameters: {"authenticity_token"=>"ocCSra0R/kcA+5MHVowZDNShghHhNUKYcO3yJaDuUKcZsRab90who4SuOK4MmS/4XXhycK0XZJ1UbS/n09aFEg==", "post_id"=>"2", "id"=>"7"}
  Topic Load (0.1ms)  SELECT  "topics".* FROM "topics" WHERE "topics"."id" = ? LIMIT 1  [["id", nil]]
Completed 404 Not Found in 7ms (ActiveRecord: 0.6ms)

Best regards.

1

1 Answers

3
votes

Derive @topic from @post because we still want redirect_to the @post page after creating or destroying a comment. Furthermore, @post is still nested under @topic.

It's saying instead of loading the topic via a Topic.find, you get it from the post. The topic_id is no longer in the params, so you have to get it from the post. No big deal since every post has a topic, right? This must be true otherwise making your routes more shallow would not be possible.

Something like this:

def destroy
  @post = Post.find(params[:post_id])
  @topic = @post.topic
  # ... the rest I think is unchanged
end

You can completely remove the @topic if you really want to, and just do this in the redirects:

redirect_to [@post.topic, @post]