Goal

You’ll build a complete, working Rails feature from scratch, directly applying models, associations, validations, routes, controllers, and views from across this entire course.

Learn

Let’s build the blog/comments feature planned conceptually in the previous lesson, combining real choices from across this whole course:

# Model (Parts 2.1, 2.3, 2.4)
class Post < ApplicationRecord
  has_many :comments, dependent: :destroy
  validates :title, presence: true
end

class Comment < ApplicationRecord
  belongs_to :post
  validates :body, presence: true
end

# Routes (Parts 1.2, 3.1, 4.3)
resources :posts do
  resources :comments, only: [:create, :destroy]
end

# Controller (Parts 1.3, 3.2, 3.4)
class CommentsController < ApplicationController
  def create
    @post = Post.find(params[:post_id])
    @comment = @post.comments.new(comment_params)
    if @comment.save
      redirect_to @post, notice: "Comment added."
    else
      redirect_to @post, alert: "Comment couldn't be saved."
    end
  end

  def destroy
    @comment = Comment.find(params[:id])
    @comment.destroy
    redirect_to @comment.post, notice: "Comment removed."
  end

  private

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

Notice how directly this reflects earlier lessons: dependent: :destroy ensures comments are cleaned up if their post is deleted, only: [:create, :destroy] applies exactly the scoped-actions reasoning from Part 6.1’s planning lesson, strong parameters (Part 3.2) protect against mass assignment, and redirect with flash messages (Part 3.4) follows the safe post-action pattern. None of this is new syntax — it’s the direct, combined application of everything already learned.

Decision Task

In the example above, why does the create action use @post.comments.new(comment_params) rather than just Comment.new(comment_params)?

Show Answer

Using @post.comments.new automatically sets the new comment’s post_id to @post’s id, correctly establishing the association (from Part 2.4) as part of creating the record — avoiding a separate step to manually set that foreign key, and ensuring the comment is genuinely and correctly associated with the right post from the moment it’s built, not just when eventually saved.

Common Mistake

Building a feature like this correctly in isolation, but forgetting details covered in earlier, separate lessons simply because they feel disconnected from “building a real feature” by the time you reach a capstone exercise — like forgetting dependent: :destroy (leaving orphaned comments if a post is deleted), or using Comment.new directly instead of the association-aware @post.comments.new. Real features need everything from every earlier lesson working together.

Practice Questions

1. Why does the Post model include dependent: :destroy on its has_many :comments association?

Show Answer

To ensure associated comments are automatically deleted if their parent post is deleted, preventing orphaned comment records left pointing at a post that no longer exists.

2. Why does the routes file use only: [:create, :destroy] for nested comments, rather than the full resources shortcut?

Show Answer

Following Part 6.1’s planning lesson — comments in this specific feature are only ever created and deleted, never individually viewed, edited, or updated via their own dedicated pages, so only those two actions are genuinely needed.

3. Why does destroy redirect to @comment.post rather than somewhere else?

Show Answer

To return the user to the post the comment belonged to, showing the post with its now-updated comment list, which is the genuinely relevant place to land after removing a comment.

4. True or False: this example’s comment_params method would technically still work correctly even without calling .require(:comment) first.

Show Answer

False — .require(:comment) is what actually raises a clear error if the comment key is entirely missing from params, and structurally scopes the subsequent .permit call correctly to that nested comment data.

5. What earlier lesson’s reasoning explains why create uses redirect_to with a flash message rather than directly rendering a view?

Show Answer

Part 3.4’s lesson on flash messages and redirects, specifically to avoid the duplicate form resubmission problem that would occur if the user refreshed a directly-rendered response.

Try It Yourself

Extend this example by adding a validates :body, length: { minimum: 3 } to the Comment model, and explain in one sentence what real problem this prevents.

Show Answer

validates :body, presence: true, length: { minimum: 3 } — this prevents genuinely low-effort, likely spam-like comments (like a single character) from being saved, adding a slightly stronger data-quality bar beyond just requiring the field isn’t blank.

Quick Check

1. Why does the Post model use dependent: :destroy?

Show Answer

To automatically delete associated comments when their parent post is deleted, preventing orphaned records.

2. Why does the route use only: [:create, :destroy] for comments?

Show Answer

Because those are the only actions this specific feature genuinely needs, following the Part 6.1 planning lesson.

3. Why does create use @post.comments.new instead of Comment.new?

Show Answer

To automatically and correctly set the post_id association as part of building the new record.

4. What does comment_params protect against?

Show Answer

Mass assignment of unintended fields, via strong parameters.

5. Why does destroy redirect to @comment.post?

Show Answer

To return the user to the relevant post, now showing its updated comment list.

تحميل هذا الباب / Download this Chapterنسخة كاملة للدراسة بدون إنترنت، مع الأسئلة والإجابات والصور المتاحة.