Rails Blog In VS Code-Post Comments
How To Create A Blog in VS Code — Part VI— RailsSeries#Episode 08
Let’s Add Comments to Each Post.
In this post:
You will:
Learn How to Add Comments
to Each Post;
Set up Action Text, which is a
framework in Ruby on Rails for
handling rich text content.
Welcome!
Let’s Get Started!
Note: If you get stuck, please see my repo.
0#step — Download the last post here and prepare your vscode
environment.
Let’s open a new Feature Branch: git checkout -b add_post_comments
and get rid of the old database:
rails db:drop
rails db:migrate
rails db:seed
bundle install
rails s
1#step —Models Associations:
The way this is going to work is a user
will have many comments
; a post
will have many comments;
a comment will belong to a specific post
as well as a specific user
so you’ll always be able to tell where a comment
is located and you’ll also be able to grab whoever the user
is and grab all their comments
and display them wherever you’d like.
Let’s create a model named comment
:
rails g model comment post:belongs_to user:belongs_to
rails db:migrate
rails g model comment post:belongs_to user:belongs_to
invoke active_record
create db/migrate/20230716133433_create_comments.rb
create app/models/comment.rb
invoke test_unit
create test/models/comment_test.rb
create test/fixtures/comments.yml
rails db:migrate
== 20230716133433 CreateComments: migrating ===================================
-- create_table(:comments)
-> 0.0037s
== 20230716133433 CreateComments: migrated (0.0037s) ==========================
2#step — Install Action Text:
rails action_text:install
rails db:migrate
bundle install --gemfile /home/j3/Documents/rails_projects/rails-blog-demo/Gemfile
rails db:migrate
rails acttion_text:install
append app/javascript/application.js
append config/importmap.rb
create app/assets/stylesheets/actiontext.css
To use the Trix editor, you must require 'app/assets/stylesheets/actiontext.css' in your base stylesheet.
create app/views/active_storage/blobs/_blob.html.erb
create app/views/layouts/action_text/contents/_content.html.erb
Ensure image_processing gem has been enabled so image uploads will work (remember to bundle!)
gsub Gemfile
rails railties:install:migrations FROM=active_storage,action_text
Copied migration 20230716140302_create_active_storage_tables.active_storage.rb from active_storage
Copied migration 20230716140303_create_action_text_tables.action_text.rb from action_text
invoke test_unit
create test/fixtures/action_text/rich_texts.yml
==============================
rails db:migrate
Could not find gem 'image_processing (~> 1.2)' in locally installed gems.
Run `bundle install --gemfile /home/j3/Documents/rails_projects/rails-blog-demo/Gemfile` to install missing gems.
==============================
bundle install --gemfile /home/j3/Documents/rails_projects/rails-blog-demo/Gemfile
Fetching gem metadata from https://rubygems.org/..........
Resolving dependencies...
...
Fetching mini_magick 4.12.0
...
Fetching ffi 1.15.5
...
Installing mini_magick 4.12.0
Installing ffi 1.15.5 with native extensions
Fetching ruby-vips 2.1.4
Installing ruby-vips 2.1.4
Fetching image_processing 1.12.2
Installing image_processing 1.12.2
Bundle complete! 18 Gemfile dependencies, 82 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
==============================
rails db:migrate
== 20230716140302 CreateActiveStorageTables: migrating ========================
-- create_table(:active_storage_blobs, {:id=>:primary_key})
-> 0.0052s
-- create_table(:active_storage_attachments, {:id=>:primary_key})
-> 0.0021s
-- create_table(:active_storage_variant_records, {:id=>:primary_key})
-> 0.0012s
== 20230716140302 CreateActiveStorageTables: migrated (0.0087s) ===============
== 20230716140303 CreateActionTextTables: migrating ===========================
-- create_table(:action_text_rich_texts, {:id=>:primary_key})
-> 0.0031s
== 20230716140303 CreateActionTextTables: migrated (0.0035s) ==================
In Rails 7, the rails action_text:install
command is used to install and set up Action Text, which is a framework in Ruby on Rails for handling rich text content. Action Text allows you to easily integrate a rich text editor into your Rails application and handle the storage and rendering of rich text content.
When you run rails action_text:install
, it performs the following actions:
- It generates a migration file that adds the necessary tables to the database schema. These tables are used to store the rich text content and attachments associated with it.
- It runs the migration, creating the required tables in the database.
- It generates a JavaScript file for configuring the rich text editor.
- It adds the necessary routes to the
config/routes.rb
file for handling the attachments uploaded through Action Text. - It adds the Action Text initializer file (
config/initializers/action_text.rb
) which configures various settings for Action Text, such as the storage service for attachments.
After running rails action_text:install
, you can start using Action Text in your Rails application. You'll be able to create rich text attributes in your models, use the provided rich text editor in your views, and handle the storage and rendering of rich text content with ease ( see step#8 ).
3#step — Let’s create a new form (_form.html.erb) inside a new folder (comments), GoTO, and type:
rails-blog-demo/app/views/comments/_form.html.erb
<%= form_with(model: [post, post.comments.build]) do |f| %>
<div class = "form-control">
<%= f.rich_text_area :body %>
<%= f.submit "Reply", class: "btn btn-primary mt-1" %>
</div>
<% end %>
The provided code snippet renders a form for submitting a new comment on a specific post. When the form is submitted, it will create a new comment associated with the given post
. The form uses Action Text's rich_text_area
to enable a rich text editor for the comment's body, providing a user-friendly way to enter and format the comment's content.
4#step — GoTo and add this block:
rails-blog-demo/config/routes.rb
...
# /post/1/comments/4
resources :posts do
resources :comments
end
...
resources :posts do ... end
: This block sets up nested routes for the posts
resource. It generates routes for CRUD operations on posts
, such as creating, reading, updating, and deleting posts
. Additionally, it also nests the comments
resource within the posts
resource, generating routes for CRUD operations on comments associated with a specific post
.
5#step — GoTo, and type below /div
:
rails-blog-demo/app/views/posts/show.html.erb
...
<div class="container">
<%= render 'comments/form', post: @post %>
</div>
In Rails 7, the code <%= render 'comments/form', post: @post %>
is used to render a partial view called 'comments/form'
and pass a local variable post
with the value of @post
.
6#step — Let’s generate comments’ controller, GoTo terminal
, and type:
rails g controller comments
create app/controllers/comments_controller.rb
invoke erb
exist app/views/comments
invoke test_unit
create test/controllers/comments_controller_test.rb
invoke helper
create app/helpers/comments_helper.rb
invoke test_unit
In Rails 7, the command rails g controller comments
is used to generate a controller file for the comments
resource. Let's break it down:
controller
: Specifies that we want to generate a controller.comments
: It is the name of the controller being generated. In this case, the controller being generated is for thecomments
resource.
When you run rails g controller comments
, Rails will generate several files and directories for the comments
controller, including:
- A controller file named
comments_controller.rb
under theapp/controllers
directory. This file will contain a skeleton structure for theCommentsController
, which you can modify and add actions to handle different requests and responses related to comments. - A view directory named
comments
under theapp/views
directory. This directory will contain view templates for thecomments
resource. By default, Rails will create an empty directory structure for different actions (e.g.,index.html.erb
,show.html.erb
,new.html.erb
, etc.), which you can populate with the appropriate HTML or ERB code. - A test file named
comments_controller_test.rb
under thetest/controllers
directory. This file will contain test cases for theCommentsController
, allowing you to write tests to ensure the controller behaves as expected.
Overall, running rails g controller comments
sets up the initial scaffolding for the comments
controller, providing you with a starting point to define the actions, views, and tests specific to handling comments in your Rails application.
7#step — GoTo, and type:
rails-blog-demo/app/controllers/comments_controller.rb
class CommentsController < ApplicationController
before_action :authenticate_user!
before_action :set_post
def create
# @post = Post.find(params[:post_id])
@comment = @post.comments.create(comment_params)
@comment.user = current_user
if @comment.save
redirect_to post_path(@post)
else
flash[:alert] = 'Comments has not been created'
redirect_to post_path(@post)
end
end
def destroy
# @post = Post.find(params[:post_id])
@comment = @post.comments.find(params[:id])
@comment.destroy
redirect_to post_path(@post)
end
private
def set_post
@post = Post.find(params[:post_id])
end
def comment_params
params.require(:comment).permit(:body)
end
end
The code we provided is a sample implementation of a CommentsController
in Rails 7. Let's go through its functionality:
- The
CommentsController
class is defined and inherited fromApplicationController
. TheApplicationController
is typically the parent controller for all other controllers in a Rails application. before_action :authenticate_user!
is a before-action filter, ensuring that the user is authenticated before executing any actions within the controller. This is typically used with authentication systems like Devise to restrict access to certain controller actions to only authenticated users.before_action :set_post
is another before-action filter that sets the@post
instance variable before certain actions are executed. This helps in keeping the code DRY (Don't Repeat Yourself) by reusing the same logic in multiple actions.- The
create
action is responsible for creating a new comment associated with a specific post. It first finds the post based on thepost_id
parameter, then creates a new comment using thecomment_params
method to permit the:body
parameter from the request. The user associated with the comment is set to the current user. If the comment saves successfully, it redirects to thepost_path(@post)
(typically the show page for the post), and if it fails to save, it sets a flash message and redirects back to the post page. - The
destroy
action is responsible for deleting a comment. It finds the comment within the specified post using thepost_id
andid
parameters. It then destroys the comment and redirects back to thepost_path(@post)
. - The
set_post
method is a private method that finds and sets the@post
instance variable based on thepost_id
parameter. This method is used as a before action to set the@post
variable before thecreate
anddestroy
actions are executed. - The
comment_params
method is a private method that defines the strong parameters for creating or updating a comment. It uses therequire
method to ensure the presence of acomment
key in the parameters and permits the:body
attribute for mass assignment.
Overall, this code represents a typical implementation of a CommentsController
in Rails 7, providing functionality for creating and deleting comments associated with a specific post, with appropriate authorization checks and redirects.
8 #step — GoTo:
rails-blog-demo/app/controllers/posts_controller.rb
add this line to show()
method:
@comments = @post.comments.order(created_at: :desc)
9#step — GoTo:
rails-blog-demo/app/models/comment.rb
class Comment < ApplicationRecord
...
has_rich_text :body
end
10#step — GoTo:
rails-blog-demo/app/models/post.rb
class Post < ApplicationRecord
...
has_many :comments, dependent: :destroy
end
11#step — GoTo:
rails-blog-demo/app/models/user.rb
class User < ApplicationRecord
...
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
end
This class definition sets up the User
model to have a one-to-many relationship with both Post
and Comment
models, allowing you to access and manipulate their associated posts and comments conveniently. The dependent: :destroy
option ensures data integrity by deleting associated records when a user is deleted, avoiding orphaned records in the database (see graph above).
12#step — Let us display a single post with associated comments, GoTo:
rails-blog-demo/app/views/posts/show.html.erb
<%= render @post %>
<div>
<%= link_to "Edit this post", edit_post_path(@post) %> |
<%= link_to "Back to posts", posts_path %>
<%= button_to "Destroy this post", @post, method: :delete %>
</div>
<div class="container">
<%= render 'comments/form', post: @post%>
<% @comments.each do |comment| %>
<%= render 'comments/comment', post: @post, comment: comment %>
<% end %>
</div>
This code snippet is part of a view template that renders a single post along with its associated comments. It includes options to edit the post, delete the post, go back to the posts list, and render a form to submit new comments. Let’s break it down a bit:
<%= render @post %>
: This line renders a partial view of the@post
object. It implies that there is a partial view file named_post.html.erb
that will be rendered, displaying the details of the post. The@post
variable should be an instance of thePost
model, and Rails will automatically find the corresponding partial view to render based on the variable name.<%= link_to "Edit this post", edit_post_path(@post) %>
: This line creates a link labeled "Edit this post." The link points to theedit
action of theposts_controller
, which should be responsible for editing a specific post. Theedit_post_path(@post)
generates the URL for the edit action of the specified post using the@post
object.<%= button_to "Destroy this post", @post, method: :delete %>
: This line generates a "Destroy this post" button. When clicked, it triggers a form submission that sends aDELETE
request to thedestroy
action of theposts_controller
. The@post
object is passed as a parameter, allowing the controller to identify and delete the specific post.<%= render 'comments/form', post: @post %>
: This line renders a partial view_form.html.erb
located inside thecomments
directory. It passes the@post
object as a local variablepost
to this partial view. This renders a form to submit a new comment associated with the given post.<% @comments.each do |comment| %>
: This line starts a loop iterating through eachcomment
object in the@comments
collection.<%= render 'comments/comment', post: @post, comment: comment %>
: This line renders a partial view_comment.html.erb
located inside thecomments
directory. It passes both the@post
andcomment
objects as local variables to this partial view. This renders the details of a single comment associated with the given post.
13#step — Create a new file /comments/_comment.html.erb
, GoTo and type:
rails-blog-demo/app/views/comments/_comment.html.erb
<div class="comment-<%= comment.id %> container"
style="border: 1px solid black; padding: 1em; margin: 1em;">
<%= comment.user.email %><br />
<span>Posted <%= time_ago_in_words(comment.created_at) %></span>
<% if current_user == comment.user %>
<div class="button-group float-end">
<%= button_to "Delete", [post, comment], class:"btn btn-danger", method: :delete %>
</div>
<% end %>
<hr />
<%= comment.body %>
</div>
The code we provided is an HTML template that displays a comment in a Rails 7 application. Let’s go through its functionality:
<div class="comment-<%= comment.id %> container" ...>
: This line creates adiv
element with a CSS class of"comment-<%= comment.id %> container"
. The CSS class will have a unique identifier based on the comment'sid
attribute, allowing for individual styling or targeting with CSS or JavaScript.<%= comment.user.email %><br />
: This line displays the email of the user who commented. It accesses theuser
association of thecomment
object and retrieves theemail
attribute.<span>Posted <%= time_ago_in_words(comment.created_at) %></span>
: This line displays the time elapsed since the comment was created. It uses thetime_ago_in_words
helper method, which takes thecreated_at
attribute of the comment and converts it into a human-readable time duration.<% if current_user == comment.user %>
: This line starts aif
condition that checks if the current user is the same as the user who commented. It compares thecurrent_user
object (representing the currently authenticated user) with theuser
association of thecomment
object.<div class="button-group float-end"> ... </div>
: This line creates andiv
element with CSS classes for styling purposes. The subsequent content will be placed inside thisdiv
if the condition in the previous step is true.<%= button_to "Delete", [post, comment], class:"btn btn-danger", method: :delete %>
: This line generates a button that triggers a delete action for the comment. It uses thebutton_to
helper method to create a button styled with CSS classes"btn btn-danger"
. The button is linked to the delete action for thecomment
object within the specifiedpost
object. The delete action will be invoked using the:delete
HTTP method.<hr />
: This line inserts a horizontal rule, which creates a visual separator between comments.<%= comment.body %>
: This line displays the body/content of the comment.
Overall, this code generates an HTML template for displaying a comment. It includes the user’s email, the time elapsed since the comment was created, and a delete button (if the current user is the owner of the comment). The comment’s body/content is also displayed.
Here is the final result:
14#step —Uploading to Heroku via GitHub merge request:
git status
git add -A
git commit -m ":lipstick: feat: Add Post Comments"
git push --set-upstream origin add_post_comments
GoTo your GitHub and Make this sequence.
Now GoTo Heroku and Destroy and Recreate Database.
GoTo Your vscode
Terminal
:
git checkout master
git status
git fetch
git pull
git status
git push heroku master
heroku addons:destroy
heroku run rails db:migrate
heroku open
That’s All, Folks!
See you in the next episode: Stimulus 👋️👋️👋️!
For your convenience:
git checkout -b add_post_comments
rails db:drop
rails db:migrate
rails db:seed
rails s
clear
rails g model comment post:belong_to user:belongs_to
rails g model comment post:belongs_to user:belongs_to
rails db:migrate
rails action_text:install
rails db:migrate
bundle install --gemfile /home/j3/Documents/rails_projects/rails-blog-demo/Gemfile
rails db:migrate
rails s
rails g controller comments
rails s
rails c
rails db:reset
rails s
clear
rails db:drop
rails db:migrate
rails db:seed
rails s
Related Posts:
00# Episode — RailsSeries — Installing Ruby on Rails Using ASDF — Why ASDF is Better Than RBENV for Rails Bootstrap App?
01# Episode — RailsSeries — How To Send Email In Rails 7? — User Registration and Onboarding.
02# Episode — RailsSeries — 14 Ruby Extensions 4 Vs Code — Based On This Deanin’s video.
03# Episode — RailsSeries — A Rails Blog In VS Code — Quick Start — How To Create A Blog in VS Code — Part I
04# Episode — RailsSeries — A Rails Blog In VS Code — Styling — How To Create A Blog in VS Code — Part II
05# Episode — RailsSeries — A Rails Blog In VS Code — Create Posts — How To Create A Blog in VS Code — Part III
06# Episode — RailsSeries — A Rails Blog In VS Code — Posts Tips&Tricks — How To Create A Blog in VS Code — Part IV
07# Episode — RailsSeries — A Rails Blog In VS Code — Devise — How To Create A Blog in VS Code — Part V
08# Episode — RailsSeries — A Rails Blog In VS Code — Add Comments to Post — How To Create A Blog in VS Code — Part VI (this one)
09# Episode — RailsSeries — Rails Blog In VS Code — Using Stimulus — How To Create A Blog in VS Code — Part VII
10# Episode — RailsSeries — Rails Blog In VS Code — Noticed V1 — Notifications for your Ruby on Rails app — Part VIII
11# Episode — RailsSeries — Rails Blog In VS Code — Noticed V2 — Notifications for your Ruby on Rails app — Part IX
For v6 let’s Tag it all!
git tag -a rails_blog_v6 -m "Blog in Rails 7 - v1.0: Go to https://j3-rails-blog-demo-5a0a55d44e12.herokuapp.com/" -m "0- Add Comments to Each Post;" -m "1- Set up Action Text, which is a framework in Ruby on Rails for handling rich text content;" -m "2- Upload Rails 7 project to Heroku." -m "Thank you for downloading this project 😘️👌️👋️😍️"
git push origin rails_blog_v6