Rails Blog In VS Code-Post Comments

How To Create A Blog in VS Code — Part VI— RailsSeries#Episode 08

J3
Jungletronics
13 min readJul 16, 2023

--

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!
Fig. 0: This post is a continuation of this one; This is a working code: 👉️rails_blog_v6

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.

Fig. 1: Made with https://excalidraw.com/ 😍️

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) ==========================
Fig. 2: This is the status of the database before action_text!

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) ==================
Fig. 3: Database after running action_text!

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:

  1. 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.
  2. It runs the migration, creating the required tables in the database.
  3. It generates a JavaScript file for configuring the rich text editor.
  4. It adds the necessary routes to the config/routes.rb file for handling the attachments uploaded through Action Text.
  5. 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:

  1. controller: Specifies that we want to generate a controller.
  2. comments: It is the name of the controller being generated. In this case, the controller being generated is for the comments 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 the app/controllers directory. This file will contain a skeleton structure for the CommentsController, which you can modify and add actions to handle different requests and responses related to comments.
  • A view directory named comments under the app/views directory. This directory will contain view templates for the comments 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 the test/controllers directory. This file will contain test cases for the CommentsController, 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:

  1. The CommentsController class is defined and inherited from ApplicationController. The ApplicationController is typically the parent controller for all other controllers in a Rails application.
  2. 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.
  3. 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.
  4. The create action is responsible for creating a new comment associated with a specific post. It first finds the post based on the post_id parameter, then creates a new comment using the comment_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 the post_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.
  5. The destroy action is responsible for deleting a comment. It finds the comment within the specified post using the post_id and id parameters. It then destroys the comment and redirects back to the post_path(@post).
  6. The set_post method is a private method that finds and sets the @post instance variable based on the post_id parameter. This method is used as a before action to set the @post variable before the create and destroy actions are executed.
  7. The comment_params method is a private method that defines the strong parameters for creating or updating a comment. It uses the require method to ensure the presence of a comment 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:

  1. <%= 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 the Post model, and Rails will automatically find the corresponding partial view to render based on the variable name.
  2. <%= link_to "Edit this post", edit_post_path(@post) %>: This line creates a link labeled "Edit this post." The link points to the edit action of the posts_controller, which should be responsible for editing a specific post. The edit_post_path(@post) generates the URL for the edit action of the specified post using the @post object.
  3. <%= 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 a DELETE request to the destroy action of the posts_controller. The @post object is passed as a parameter, allowing the controller to identify and delete the specific post.
  4. <%= render 'comments/form', post: @post %>: This line renders a partial view _form.html.erb located inside the comments directory. It passes the @post object as a local variable post to this partial view. This renders a form to submit a new comment associated with the given post.
  5. <% @comments.each do |comment| %>: This line starts a loop iterating through each comment object in the @comments collection.
  6. <%= render 'comments/comment', post: @post, comment: comment %>: This line renders a partial view _comment.html.erb located inside the comments directory. It passes both the @post and comment 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:

  1. <div class="comment-<%= comment.id %> container" ...>: This line creates a div element with a CSS class of "comment-<%= comment.id %> container". The CSS class will have a unique identifier based on the comment's id attribute, allowing for individual styling or targeting with CSS or JavaScript.
  2. <%= comment.user.email %><br />: This line displays the email of the user who commented. It accesses the user association of the comment object and retrieves the email attribute.
  3. <span>Posted <%= time_ago_in_words(comment.created_at) %></span>: This line displays the time elapsed since the comment was created. It uses the time_ago_in_words helper method, which takes the created_at attribute of the comment and converts it into a human-readable time duration.
  4. <% if current_user == comment.user %>: This line starts a if condition that checks if the current user is the same as the user who commented. It compares the current_user object (representing the currently authenticated user) with the user association of the comment object.
  5. <div class="button-group float-end"> ... </div>: This line creates an div element with CSS classes for styling purposes. The subsequent content will be placed inside this div if the condition in the previous step is true.
  6. <%= 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 the button_to helper method to create a button styled with CSS classes "btn btn-danger". The button is linked to the delete action for the comment object within the specified post object. The delete action will be invoked using the :delete HTTP method.
  7. <hr />: This line inserts a horizontal rule, which creates a visual separator between comments.
  8. <%= 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:

Fig. 4: On https://localhost:3000

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
Fig. 5: On Heroku view. You can browse all the code in my gitHub.

That’s All, Folks!

See you in the next episode: Stimulus 👋️👋️👋️!

👉️ rails_blog_v6

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 CodePart 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

--

--

J3
Jungletronics

😎 Gilberto Oliveira Jr | 🖥️ Computer Engineer | 🐍 Python | 🧩 C | 💎 Rails | 🤖 AI & IoT | ✍️