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.