Rails Blog In VS Code-Noticed V2

How To Create A Blog in VS Code — Part IX— Notifications for your Ruby on Rails app — RailsSeries#Episode 11

J3
Jungletronics

--

Greetings! Exciting news — we’ve done it!

Our Blog demo version 8 is now up and running with Noticed v2. After experimenting with v1, we’re thrilled to introduce you to our improved solution: Noticed v2!

Join us in this post as we walk you through the process of upgrading from the previous version and getting Noticed v2 running smoothly on our blog.

Welcome aboard!

In case you’re not yet familiar, Noticed V2 is a remarkable gem developed by Chris Oliver. It simplifies the transmission of notifications of various types through different channels to diverse recipients within your application.

Here it is functioning well:

Here is the example we will be developing — About This Episode: Notifications in Rails applications are even easier now with the new release of Noticed Gem v2. We’ll explore how to add notifications to your Rails app in just a few steps. Welcome!

In this post (based on Deanin):

You will:

Learn How to Use Noticed version 2.2;

Learn how to use Bootstrap Badges;

Set up Turbo JavaScript to review a comment;

Enhance your blog by implementing comment notifications;

Create a bell icon in your navigation bar;

Counter to keep track of unread notifications.

Additionally, display read notifications in the dropdown menu.

Each comment notification will direct you to the
corresponding blog post and provide information
about the user who sent the notification.

Welcome!
Welcome! This graphic illustrates all the relationships within the business and the Noticed v2 models. Feel free to download the SVG to navigate easily through the concepts. Or the PNG version. Improving understanding through the establishment of connections between Oliver’s concepts 👌. ️We hope you find it helpful!

Let’s Get Started!

Note: if you get stuck, please see my repo.

0#step — Download the last version (v7) post here and prepare your vscode environment.

Let’s open a new Feature Branch: git checkout -b use_noticed_gem_2 .

1#step — On Terminal, Run:

bundle add noticed -v 2.2

2#step — Now, on Terminal, type:

rails noticed:install:migrations
bundle install
rails db:migrate
Output:

rails db:migrate
== 20240329223906 CreateNoticedTables: migrating ==============================
-- create_table(:noticed_events, {:id=>:primary_key})
-> 0.0016s
-- create_table(:noticed_notifications, {:id=>:primary_key})
-> 0.0015s
== 20240329223906 CreateNoticedTables: migrated (0.0032s) =====================

== 20240329223907 AddNotificationsCountToNoticedEvent: migrating ==============
-- add_column(:noticed_events, :notifications_count, :integer)
-> 0.0014s
== 20240329223907 AddNotificationsCountToNoticedEvent: migrated (0.0014s) =====

3#step — Then, open this file and type:

app/models/user.rb

  has_many :comments, dependent: :destroy
# By implementing this feature, users will be able to conveniently
# associate and access all notifications directed towards them.
has_many :notifications, as: :recipient, dependent: :destroy, class_name: 'Noticed::Notification'
# Whenever you have noticed events that have the record pointing to user,
# such as when a new user joins a team or any similar occurrences,
# it's important to ensure that notifications mentioning us are accessible.
has_many :notification_mentions, as: :record, dependent: :destroy, class_name: 'Noticed::Event'

4#step — On Terminal:

rails g noticed:notifier CommentNotifier
Output:

create app/notifiers/application_notifier.rb
create app/notifiers/comment_notifier.rb

5#step — Open this file and type:

app/models/post.rb

has_many :notifications, through: :user, dependent: :destroy
has_many :notification_mentions, through: :user, dependent: :destroy
has_noticed_notifications model_name: 'Noticed::Notification'

6#step —Open this file and delete this line:

app/views/posts/index.html.erb

<p style="color: green"><%= notice %></p>

<h1>Posts</h1>
.....
This will eradicate redundant messages.

7#step — Open this file and type:

app/models/comment.rb

...
after_create_commit :notify_recipient
before_destroy :cleanup_notifications
has_noticed_notifications model_name: 'Noticed::Event'
has_many :notification_mentions, as: :record, dependent: :destroy, class_name: 'Noticed::Event'

private

def notify_recipient
CommentNotifier.with(record: self, post:).deliver_later(post.user)
# Using deliver_later will execute a background job when you hit 'post' for your comment.
# This means that it won't stall the interface while the job is being processed.
# You can simply post your comment and continue with your tasks, while in the background,
# the Rails system takes care of delivering the comment in the background.
end

def cleanup_notifications
post.notifications.where(id: post.id).destroy_all

# post.notifications.count -> [3]
# post.notifications.where(recipient_id: 1) - > #<ActiveRecord::AssociationRelation []>
# same as in console: Notification.where(comment_id: :comment_id)
end
...

8#step — GoTo:

noticed_2/app/controllers/comments_controller.rb

class CommentsController < ApplicationController
before_action :authenticate_user!
before_action :set_post
...

def destroy
@comment = @post.comments.find(params[:id])

# # Step 1: Delete records from ActionText::RichText
ActionText::RichText.where(record_type: 'Comment', record_id: @comment.id).destroy_all
# Step 2: Manually delete associated records from noticed_events and noticed_notifications tables
Noticed::Event.where(record_type: 'Comment', record_id: @comment.id ).destroy_all
Noticed::Notification.where( recipient_type: 'User', recipient_id: current_user.id, event_id: @comment.id ).destroy_all


@comment.delete
redirect_to post_path(@post)
end

....

end

9#step — Open this file and uncomment delivering by DB, defining def message and def URL methods:

app/notifiers/comment_notifier.rb

# To deliver this notification:
#
# CommentNotifier.with(record: @post, message: "New post").deliver(User.all)

class CommentNotifier < ApplicationNotifier
# Add your delivery methods
#
# deliver_by :email do |config|
# config.mailer = "UserMailer"
# config.method = "new_comment_email"
# config.params = ->{ params }
# config.args = ->{ [1, 2, 3] }
# end

#
# bulk_deliver_by :slack do |config|
# config.url = -> { Rails.application.credentials.slack_webhook_url }
# end
#
# deliver_by :custom do |config|
# config.class = "MyDeliveryMethod"
# end

# Add required params
#
# required_param :message
# Define helper methods to make rendering easier.
def message
# test
# @post = CommentNotifier.find(id).params[:post]
# @user = User.find((CommentNotifier.find(id).params[:post].user_id))
@post = record.post
@user = record.user
"#{@user.name} replied to #{@post.title.truncate(14)}"
end

# That allows us to do our url construction
def url
# test
# post_path(CommentNotifier.find(id).params[:post].id)
post_path(record.post.id)
end
end

10#step —To preview how notifications appear, run the application and create some comments. Afterward, launch the rails console.

Below are 17 commands for your convenience. Reviewing all the output provided can be invaluable for debugging purposes.

Noticed::Notification.all
Noticed::Event.all
CommentNotifier.all
CommentNotifier.newest_first
Comment.all
Comment._reflections
Comment.last.notification_mentions
Post._reflections
Post.last.notifications
Post.first.notifications.event
Post.last.notifications.first.event_id
Noticed::Event.first
Noticed::Notification.first
User.first.notifications
User.find(User.first.notifications.first.recipient_id).email
User.first.email
Comment.find(User.first.notifications.first.event_id)
Comment.first
User.find(Comment.first.notification_mentions.first.params[:post].user_id).email

We navigate through the majority of the connections between the noticed_events and noticed_notifications. This is crucial for comprehending the underlying framework of Oliver’s solutions.

You can view all these outputs from my Ubuntu 23 (Lunar Lobster) in the section Notes & Data Explorations below [TODO: GET THE INTERNAL LINK].

11#step —Navigate to the “Bootstrap 5 — Icons — Learn More” link and copy the CDN provided on the webpage: https://icons.getbootstrap.com/. Paste the copied CDN directly below the Bootstrap 5 link in your file. Then, open the file:

app/views/layouts/application.html.erb

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">

12#step — Open the Navbar file and insert this rendering command above the session manager:

app/views/layouts/_navbar.html.erb

<%= render 'layouts/notifications' %>

...above here...
<%= render 'user/session_manager' %>

13#step — Create this file and type:

Capturing attention towards the render method is crucial.

app/views/layouts/_notifications.html.erb

<% if current_user %>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<span class="badge rounded-pill bg-secondary">
<%= @unread.count >= 9 ? "9+" : @unread.count %> <i class="bi bi-bell-fill"></i>
</span>
</a>
<ul class="dropdown-menu dropdown-menu-lg-end" aria-labelledby="navbarDropdown">
<% @unread.each do |notification| %>
<%= render 'layouts/notification', notification: notification %>
<% end %>
<% if @read.count > 0 && @unread.count > 0 %>
<li>
<hr class="dropdown-divider">
</li>
<% elsif @read.count + @unread.count == 0 %>
<li class="dropdown-item">
No notifications yet.
</li>
<% end %>
<% @read.each do |notification| %>
<%= render 'layouts/notification', notification: notification %>
<% end %>
</ul>
</li>
<% end %>

14#step — Create this partial too:

app/views/layouts/_notification.html.erb

<li class="dropdown-item">
<li>
<%= link_to notification.event.message, notification.event.url %>
</li>
</li>

15#step — GoTo :

app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
before_action :set_notifications, if: :current_user

private

def set_notifications
notifications = Noticed::Notification.where(recipient: current_user).newest_first.limit(9)
@unread = current_user.notifications.unread
@read = current_user.notifications.read
end
end

16#step — GoTo:

app/controllers/posts_controller.rb

In the show() method, type:

mark_notifications_as_read

In the destroy() method, modify @post.destroy by typing the appropriate code:

    @post.comments.destroy_all
@post.delete

And in the private area, create this method:

def mark_notifications_as_read
return unless current_user

notifications_to_mark_as_read = @post.notifications.where(recipient: current_user)
notifications_to_mark_as_read.update_all(read_at: Time.zone.now)
end

17#step — GoTo, surrounding the destroy button:

/app/views/posts/show.html.erb

<%= render @post %>
<div>
...

<% if current_user == @post.user %>
<%= button_to "Destroy this post", @post, method: :delete %>
<% end %>

...

</div>

This encapsulates the destroy button within logic to verify if the requester is the current user. As a result, the destroy option is now exclusively visible to the owner.

18#step — For those who’ve caught on, that’s all! 😎️

Now, onto the final episode, where the interface button shifts from Edit to Cancel with a simple toggle.

GoTo:

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 />
<% if (comment.updated_at - comment.created_at ) > 1 %>
<span>Edited <%= time_ago_in_words(comment.updated_at) %> ago</span>
<% else %>
<span>Posted <%= time_ago_in_words(comment.created_at) %> ago</span>
<% end %>
<% if current_user == comment.user %>
<div class="btn-group float-end">
<%= link_to "Edit", nil, remote: true, class: "btn btn-warning",
data: {
controller: "comments",
action: "comments#toggleForm",
comments_form_param: "edit-form-#{comment.id}",
comments_body_param: "comment-body-#{comment.id}",
comments_edit_param: "edit-button-#{comment.id}"},
id: "edit-button-#{comment.id}" %>
<%= button_to "Delete", [post, comment], class:"btn btn-danger", method: :delete %>
</div>
<div id="edit-form-<%= comment.id %>" class="d-none" >
<%= render 'comments/form', post: @post, comment: comment, submit_label: "Update" %>
</div>
<% end %>
<hr />
<div id="comment-body-<%= comment.id %>">
<%= comment.body %>
</div>
</div>

19#step — Go to:

app/javascript/controllers/comments_controller.js

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
initialize() {}
connect() {}
toggleForm(event) {
console.log("I clicked the edit button.");
event.preventDefault();
event.stopPropagation();
const formID = event.params["form"];
const commentBodyID = event.params["body"];
const editButtonID = event.params["edit"];

const form = document.getElementById(formID);
const commentBody = document.getElementById(commentBodyID);
const editButton = document.getElementById(editButtonID);

form.classList.toggle("d-none");
form.classList.toggle("mt-5");
commentBody.classList.toggle("d-none");
this.toggleEditButton(editButton);
}

toggleEditButton(editButton) {
if (editButton.innerText === "Edit") {
editButton.innerText = "Cancel";
this.toggleEditButtonClass(editButton);
} else {
editButton.innerText = "Edit";
this.toggleEditButtonClass(editButton);
}
}

toggleEditButtonClass(editButton) {
editButton.classList.toggle("btn-secondary");
editButton.classList.toggle("btn-warning");
}
}

Run and hope for the best!

Behold the final result! Now, immerse yourself in Oliver’s Noticed v2 to grasp its essence. With this “hello world,” your study sessions will be infinitely more productive. I guarantee it! Thank you for stopping by. Stay tuned for the next installment. Farewell!
That’s All, Folks!

I would like to express my gratitude to Jeovan Farias and Bruno Vichinheski for dedicating their time to debugging this app and helping me learn more about Rails. Thank you both!

Stay tuned for the next episode where we delve into Ransack 4 👋️👋️👋️!

👉️ noticed_v2

This is my endeavor to follow Chris Oliver’s Noticed v2 tutorial. Chris Oliver is renowned as the creator of GoRails, Hatchbox.io, and Jumpstart. Devoting his time to crafting tutorials and developing tools, he assists Ruby on Rails developers in building applications more efficiently. For further insights, you can explore Chris Oliver’s GitHub.

Notes & Data Explorations:

Here is some image from dbeaver-ce:

Table: posts
Table: users
Table: comments
Table: action_text_rich_texts
Table: noticed_events
Table: noticed_notifications
Noticed::Notification.all
Noticed::Notification.all
Noticed::Notification Load (0.1ms) SELECT "noticed_notifications".* FROM "noticed_notifications"
=>
[#<CommentNotifier::Notification:0x00007f8aeb2a3708
id: 1,
type: "CommentNotifier::Notification",
event_id: 1,
recipient_type: "User",
recipient_id: 1,
read_at: Fri, 29 Mar 2024 18:43:57.873095000 UTC +00:00,
seen_at: nil,
created_at: Fri, 29 Mar 2024 18:43:57.832000000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:43:57.832000000 UTC +00:00>,
#<CommentNotifier::Notification:0x00007f8ae9e364a0
id: 2,
type: "CommentNotifier::Notification",
event_id: 2,
recipient_type: "User",
recipient_id: 2,
read_at: Fri, 29 Mar 2024 18:45:00.706113000 UTC +00:00,
seen_at: nil,
created_at: Fri, 29 Mar 2024 18:45:00.673000000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:45:00.673000000 UTC +00:00>]
Noticed::Event.all
Noticed::Event.all
Noticed::Event Load (0.1ms) SELECT "noticed_events".* FROM "noticed_events"
Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
=>
[#<CommentNotifier:0x00007f8aeb517de0
id: 1,
type: "CommentNotifier",
record_type: "Comment",
record_id: 1,
params:
{:post=>
#<Post:0x00007f8ae9b87808
id: 1,
title: "First Post",
body: "Body 1.",
created_at: Fri, 29 Mar 2024 18:43:26.920836000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:45:13.934527000 UTC +00:00,
views: 3,
user_id: 1>},
created_at: Fri, 29 Mar 2024 18:43:57.823635000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:43:57.823635000 UTC +00:00,
notifications_count: 1>,
#<CommentNotifier:0x00007f8aeb4a9cf0
id: 2,
type: "CommentNotifier",
record_type: "Comment",
record_id: 2,
params:
{:post=>
#<Post:0x00007f8aea53cc68
id: 2,
title: "Second Post",
body: "Body 2.",
created_at: Fri, 29 Mar 2024 18:44:44.264450000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:45:00.700451000 UTC +00:00,
views: 2,
user_id: 2>},
created_at: Fri, 29 Mar 2024 18:45:00.668426000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:45:00.668426000 UTC +00:00,
notifications_count: 1>]
CommentNotifier.all
CommentNotifier.all
CommentNotifier Load (0.1ms) SELECT "noticed_events".* FROM "noticed_events" WHERE "noticed_events"."type" = ? [["type", "CommentNotifier"]]
Post Load (0.0ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
=>
[#<CommentNotifier:0x00007f8aeb5626d8
id: 1,
type: "CommentNotifier",
record_type: "Comment",
record_id: 1,
params:
{:post=>
#<Post:0x00007f8ae946f480
id: 1,
title: "First Post",
body: "Body 1.",
created_at: Fri, 29 Mar 2024 18:43:26.920836000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:45:13.934527000 UTC +00:00,
views: 3,
user_id: 1>},
created_at: Fri, 29 Mar 2024 18:43:57.823635000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:43:57.823635000 UTC +00:00,
notifications_count: 1>,
#<CommentNotifier:0x00007f8aeb562638
id: 2,
type: "CommentNotifier",
record_type: "Comment",
record_id: 2,
params:
{:post=>
#<Post:0x00007f8ae946cdc0
id: 2,
title: "Second Post",
body: "Body 2.",
created_at: Fri, 29 Mar 2024 18:44:44.264450000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:45:00.700451000 UTC +00:00,
views: 2,
user_id: 2>},
created_at: Fri, 29 Mar 2024 18:45:00.668426000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:45:00.668426000 UTC +00:00,
notifications_count: 1>]
CommentNotifier.newest_first
CommentNotifier.newest_first
CommentNotifier Load (0.1ms) SELECT "noticed_events".* FROM "noticed_events" WHERE "noticed_events"."type" = ? ORDER BY "noticed_events"."created_at" DESC [["type", "CommentNotifier"]]
Post Load (0.0ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=>
[#<CommentNotifier:0x00007f8aeb503520
id: 2,
type: "CommentNotifier",
record_type: "Comment",
record_id: 2,
params:
{:post=>
#<Post:0x00007f8aeb500c80
id: 2,
title: "Second Post",
body: "Body 2.",
created_at: Fri, 29 Mar 2024 18:44:44.264450000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:45:00.700451000 UTC +00:00,
views: 2,
user_id: 2>},
created_at: Fri, 29 Mar 2024 18:45:00.668426000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:45:00.668426000 UTC +00:00,
notifications_count: 1>,
#<CommentNotifier:0x00007f8aeb503480
id: 1,
type: "CommentNotifier",
record_type: "Comment",
record_id: 1,
params:
{:post=>
#<Post:0x00007f8aeb569618
id: 1,
title: "First Post",
body: "Body 1.",
created_at: Fri, 29 Mar 2024 18:43:26.920836000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:45:13.934527000 UTC +00:00,
views: 3,
user_id: 1>},
created_at: Fri, 29 Mar 2024 18:43:57.823635000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:43:57.823635000 UTC +00:00,
notifications_count: 1>]
Comment.all
Comment.all
Comment Load (0.1ms) SELECT "comments".* FROM "comments"
=>
[#<Comment:0x00007f8ae9a2d980
id: 1,
post_id: 1,
user_id: 1,
created_at: Fri, 29 Mar 2024 18:43:57.766464000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:43:57.788534000 UTC +00:00>,
#<Comment:0x00007f8ae9326a60
id: 2,
post_id: 2,
user_id: 2,
created_at: Fri, 29 Mar 2024 18:45:00.650325000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:45:00.654811000 UTC +00:00>]
Comment._reflections
Comment._reflections
=>
{"post"=>
#<ActiveRecord::Reflection::BelongsToReflection:0x00007f8ae94a27b8
@active_record=
Comment(id: integer, post_id: integer, user_id: integer, created_at: datetime, updated_at: datetime),
@klass=nil,
@name=:post,
@options={},
@plural_name="posts",
@scope=nil>,
"user"=>
#<ActiveRecord::Reflection::BelongsToReflection:0x00007f8ae94a1c78
@active_record=
Comment(id: integer, post_id: integer, user_id: integer, created_at: datetime, updated_at: datetime),
@klass=nil,
@name=:user,
@options={},
@plural_name="users",
@scope=nil>,
"rich_text_body"=>
#<ActiveRecord::Reflection::HasOneReflection:0x00007f8ae94a0c38
@active_record=
Comment(id: integer, post_id: integer, user_id: integer, created_at: datetime, updated_at: datetime),
@klass=nil,
@name=:rich_text_body,
@options=
{:class_name=>"ActionText::RichText",
:as=>:record,
:inverse_of=>:record,
:autosave=>true,
:dependent=>:destroy},
@plural_name="rich_text_bodies",
@scope=
#<Proc:0x00007f8ae9830650 /home/j3/.asdf/installs/ruby/3.2.2/lib/ruby/gems/3.2.0/gems/activerecord-7.0.6/lib/active_record/associations/builder/association.rb:55>,
@type="record_type">,
"notification_mentions"=>
#<ActiveRecord::Reflection::HasManyReflection:0x00007f8ae94af8c8
@active_record=
Comment(id: integer, post_id: integer, user_id: integer, created_at: datetime, updated_at: datetime),
@klass=nil,
@name=:notification_mentions,
@options={:as=>:record, :dependent=>:destroy, :class_name=>"Noticed::Event"},
@plural_name="notification_mentions",
@scope=nil,
@type="record_type">}
Comment.last.notification_mentions
Comment.last.notification_mentions
Comment Load (0.2ms) SELECT "comments".* FROM "comments" ORDER BY "comments"."id" DESC LIMIT ? [["LIMIT", 1]]
Noticed::Event Load (0.2ms) SELECT "noticed_events".* FROM "noticed_events" WHERE "noticed_events"."record_id" = ? AND "noticed_events"."record_type" = ? [["record_id", 2], ["record_type", "Comment"]]
Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
=>
[#<CommentNotifier:0x00007f8ae948c760
id: 2,
type: "CommentNotifier",
record_type: "Comment",
record_id: 2,
params:
{:post=>
#<Post:0x00007f8ae948abe0
id: 2,
title: "Second Post",
body: "Body 2.",
created_at: Fri, 29 Mar 2024 18:44:44.264450000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:45:00.700451000 UTC +00:00,
views: 2,
user_id: 2>},
created_at: Fri, 29 Mar 2024 18:45:00.668426000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:45:00.668426000 UTC +00:00,
notifications_count: 1>]
Post._reflections
Post._reflections
=>
{"user"=>
#<ActiveRecord::Reflection::BelongsToReflection:0x00007f8aeb309c60
@active_record=
Post(id: integer, title: string, body: text, created_at: datetime, updated_at: datetime, views: integer, user_id: integer),
@klass=nil,
@name=:user,
@options={},
@plural_name="users",
@scope=nil>,
"comments"=>
#<ActiveRecord::Reflection::HasManyReflection:0x00007f8aeb33d308
@active_record=
Post(id: integer, title: string, body: text, created_at: datetime, updated_at: datetime, views: integer, user_id: integer),
@klass=nil,
@name=:comments,
@options={:dependent=>:destroy},
@plural_name="comments",
@scope=nil>,
"notifications"=>
#<ActiveRecord::Reflection::ThroughReflection:0x00007f8aeb0510b8
@delegate_reflection=
#<ActiveRecord::Reflection::HasManyReflection:0x00007f8aeb331a58
@active_record=
Post(id: integer, title: string, body: text, created_at: datetime, updated_at: datetime, views: integer, user_id: integer),
@klass=nil,
@name=:notifications,
@options={:through=>:user, :dependent=>:destroy},
@plural_name="notifications",
@scope=nil>,
@klass=nil,
@source_reflection_name=nil>,
"notification_mentions"=>
#<ActiveRecord::Reflection::ThroughReflection:0x00007f8ae9b9d1a8
@delegate_reflection=
#<ActiveRecord::Reflection::HasManyReflection:0x00007f8aebdeea18
@active_record=
Post(id: integer, title: string, body: text, created_at: datetime, updated_at: datetime, views: integer, user_id: integer),
@klass=nil,
@name=:notification_mentions,
@options={:through=>:user, :dependent=>:destroy},
@plural_name="notification_mentions",
@scope=nil>,
@klass=nil,
@source_reflection_name=nil>}
Post.last.notifications
Post.last.notifications
Post Load (0.1ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."id" DESC LIMIT ? [["LIMIT", 1]]
Noticed::Notification Load (0.1ms) SELECT "noticed_notifications".* FROM "noticed_notifications" INNER JOIN "users" ON "noticed_notifications"."recipient_id" = "users"."id" WHERE "users"."id" = ? AND "noticed_notifications"."recipient_type" = ? [["id", 2], ["recipient_type", "User"]]
=>
[#<CommentNotifier::Notification:0x00007f8ae9413180
id: 2,
type: "CommentNotifier::Notification",
event_id: 2,
recipient_type: "User",
recipient_id: 2,
read_at: Fri, 29 Mar 2024 18:45:00.706113000 UTC +00:00,
seen_at: nil,
created_at: Fri, 29 Mar 2024 18:45:00.673000000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:45:00.673000000 UTC +00:00>]

Post.last.notifications.first.event_id
Post.last.notifications.first.event_id
Post Load (0.1ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."id" DESC LIMIT ? [["LIMIT", 1]]
Noticed::Notification Load (0.1ms) SELECT "noticed_notifications".* FROM "noticed_notifications" INNER JOIN "users" ON "noticed_notifications"."recipient_id" = "users"."id" WHERE "users"."id" = ? AND "noticed_notifications"."recipient_type" = ? ORDER BY "noticed_notifications"."id" ASC LIMIT ? [["id", 2], ["recipient_type", "User"], ["LIMIT", 1]]
=> 2
Noticed::Event.first
Noticed::Event.first
Noticed::Event Load (0.1ms) SELECT "noticed_events".* FROM "noticed_events" ORDER BY "noticed_events"."id" ASC LIMIT ? [["LIMIT", 1]]
Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=>
#<CommentNotifier:0x00007f8ae934cc60
id: 1,
type: "CommentNotifier",
record_type: "Comment",
record_id: 1,
params:
{:post=>
#<Post:0x00007f8ae934a780
id: 1,
title: "First Post",
body: "Body 1.",
created_at: Fri, 29 Mar 2024 18:43:26.920836000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:45:13.934527000 UTC +00:00,
views: 3,
user_id: 1>},
created_at: Fri, 29 Mar 2024 18:43:57.823635000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:43:57.823635000 UTC +00:00,
notifications_count: 1>
Noticed::Notification.first
Noticed::Notification.first
Noticed::Notification Load (0.1ms) SELECT "noticed_notifications".* FROM "noticed_notifications" ORDER BY "noticed_notifications"."id" ASC LIMIT ? [["LIMIT", 1]]
=>
#<CommentNotifier::Notification:0x00007f8aeb4a67d0
id: 1,
type: "CommentNotifier::Notification",
event_id: 1,
recipient_type: "User",
recipient_id: 1,
read_at: Fri, 29 Mar 2024 18:43:57.873095000 UTC +00:00,
seen_at: nil,
created_at: Fri, 29 Mar 2024 18:43:57.832000000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:43:57.832000000 UTC +00:00>
User.first.notifications
User.first.notifications
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
Noticed::Notification Load (0.2ms) SELECT "noticed_notifications".* FROM "noticed_notifications" WHERE "noticed_notifications"."recipient_id" = ? AND "noticed_notifications"."recipient_type" = ? [["recipient_id", 1], ["recipient_type", "User"]]
=>
[#<CommentNotifier::Notification:0x00007f8ae93121c8
id: 1,
type: "CommentNotifier::Notification",
event_id: 1,
recipient_type: "User",
recipient_id: 1,
read_at: Fri, 29 Mar 2024 18:43:57.873095000 UTC +00:00,
seen_at: nil,
created_at: Fri, 29 Mar 2024 18:43:57.832000000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:43:57.832000000 UTC +00:00>]
User.find(User.first.notifications.first.recipient_id).email
User.find(User.first.notifications.first.recipient_id).email
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
Noticed::Notification Load (0.1ms) SELECT "noticed_notifications".* FROM "noticed_notifications" WHERE "noticed_notifications"."recipient_id" = ? AND "noticed_notifications"."recipient_type" = ? ORDER BY "noticed_notifications"."id" ASC LIMIT ? [["recipient_id", 1], ["recipient_type", "User"], ["LIMIT", 1]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> "giljr.2009@gmail.com"
User.first.email
User.first.email
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
=> "giljr.2009@gmail.com"
Comment.find(User.first.notifications.first.event_id)
Comment.find(User.first.notifications.first.event_id)
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
Noticed::Notification Load (0.1ms) SELECT "noticed_notifications".* FROM "noticed_notifications" WHERE "noticed_notifications"."recipient_id" = ? AND "noticed_notifications"."recipient_type" = ? ORDER BY "noticed_notifications"."id" ASC LIMIT ? [["recipient_id", 1], ["recipient_type", "User"], ["LIMIT", 1]]
Comment Load (0.0ms) SELECT "comments".* FROM "comments" WHERE "comments"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=>
#<Comment:0x00007f8ae936f968
id: 1,
post_id: 1,
user_id: 1,
created_at: Fri, 29 Mar 2024 18:43:57.766464000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:43:57.788534000 UTC +00:00>
Comment.first
Comment.first
Comment Load (0.2ms) SELECT "comments".* FROM "comments" ORDER BY "comments"."id" ASC LIMIT ? [["LIMIT", 1]]
=>
#<Comment:0x00007f8ae89e21c0
id: 1,
post_id: 1,
user_id: 1,
created_at: Fri, 29 Mar 2024 18:43:57.766464000 UTC +00:00,
updated_at: Fri, 29 Mar 2024 18:43:57.788534000 UTC +00:00>
irb(main):115:0> Comment.first
User.find(Comment.first.notification_mentions.first.params[:post].user_id).email
User.find(Comment.first.notification_mentions.first.params[:post].user_id).email
Comment Load (0.1ms) SELECT "comments".* FROM "comments" ORDER BY "comments"."id" ASC LIMIT ? [["LIMIT", 1]]
Noticed::Event Load (0.1ms) SELECT "noticed_events".* FROM "noticed_events" WHERE "noticed_events"."record_id" = ? AND "noticed_events"."record_type" = ? ORDER BY "noticed_events"."id" ASC LIMIT ? [["record_id", 1], ["record_type", "Comment"], ["LIMIT", 1]]
Post Load (0.0ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> "giljr.2009@gmail.com"

Credits & References

Jeovan Farias — A seasoned experimenter in senior program development with vast expertise spanning over 6 years in Rails. Currently employed at SEFIN RO — linkedin

Bruno Vichinheski — Graduate from Universidade Tecnológica Federal do Paraná. He is also a seasoned programmer proficient in Rails and various other languages. Currently employed at SEFIN RO. — linkedin

Add Comment Notifications To Your Blog | Ruby On Rails For Beginners Part 5 by Deanin

Add Comment Notifications To Your Blog | Ruby On Rails For Beginners Part 5 by Deanin

Bootstrap5 Badges by getbootstrap.com

Noticed — 1.5.7Docs by rubydoc.info/gems/noticed/1.5.7 (this tut uses it)

Noticed — 2.1.3Docs by rubydoc.info/gems/noticed/2.1.3 (the next one, soon…)

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

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 (this one)

This is an explanation by Chris Oliver about Noticed v2. Chris Oliver is renowned as the creator of GoRails, Hatchbox.io, and Jumpstart. Devoting his time to crafting tutorials and developing tools, he assists Ruby on Rails developers in building applications more efficiently. For further insights, you can explore Chris Oliver’s GitHub.

For v2 let’s Tag it all!

git tag -a rails_blog_v8 -m "Blog in Rails 8- v1.0:  Go to  https://medium.com/jungletronics/rails-blog-in-vs-code-noticed-v2-7ab37f9d5cc4" -m "0- Learn How to Use Noticed version 2.2;" -m "1- Enhance your blog by implementing comment notifications;" -m "2- Learn how to use Bootstrap Badges;" -m "3- Set up Turbo JavaScript to review a comment;" -m "4- Counter to keep track of read/unread notifications." -m "5- Find compelling graphics that effectively illustrate the connections between the key concepts of Oliver's Noticed Framework. Let's enhance the visual representation to highlight the depth and significance of these concepts."-m "Thank you for downloading this project 😍️" 
git push origin rails_blog_v8
[GIT CMDS]

[LINUX]

git clone -b rails_blog_v7 git@github.com:giljr/rails_blog_demo.git
cd rails_blog_v7
code .

[VSCODE]
git checkout -b use_noticed_gem_2
bundle add noticed -v 2.2
bundle install
rails noticed:install:migrations
bundle install
rails db:migrate
rails g noticed:notifier CommentNotifier
rails s
rails c
rails s
rails db:drop
rails db:migrate
rails s
git status
git add -A
git commit -m ":lipstick: Upload to Noticed v2"
git push
git push --set-upstream origin use_noticed_gem_2

[GoTo your GIT REPO and Merge the Request]

git checkout master
git status
git fetch
git pull
rails s

--

--

J3
Jungletronics

Hi, Guys o/ I am J3! I am just a hobby-dev, playing around with Python, Django, Ruby, Rails, Lego, Arduino, Raspy, PIC, AI… Welcome! Join us!