Introduction
Action cable has Rails integrated WebSocket to support realtime written in Ruby to allow two-way communication between client and server. Data is transmitted via HTTP (Ajax) protocol.
It allows writing real-time features. It provides a full range of features to support the link between client-side javascrip framework and sever-side Rail framework.
Notification
Step 1
- Create the Notification table
1 2 3 | rails g model Notification sender_id:int content:string receiver_id:int rails g model User name:string |
Establish a relationship model
1 2 3 4 5 6 | #app/models/user.rb has_many :notifications, class_name: Notification.name, foreign_key: :receiver_id, dependent: :destroy has_many :send_notifications, class_name: Notification.name, foreign_key: :sender_id, dependent: :destroy |
1 2 3 4 | #app/models/notification.rb belongs_to :sender, class_name: User.name belongs_to :receiver, class_name: User.name |
Step 2
- Create view
1 2 3 4 5 6 7 8 9 | #app/views/notifications/_notification_center.html.erb <div class="nav-item dropdown"> <%= render "notifications/counter", counter: current_user.notifications.count %> <ul id="notification-list"> <%= render notifications %> </ul> </div> |
- Partial View when new Notification is available
1 2 3 4 5 6 | #app/views/notifications/_notification.html.erb <li> <%= notification.event %> <span> <%= notification.created_at.strftime("%d %b. %Y") %></span> </li> |
- Partial View to count the number of Notifcation
1 2 3 4 | #app/views/notifications/_counter.html.erb <i class="far fa-bell></i> <span id="notification-counter"><%= counter %></span> |
Step 3
- ActionCable will allow you to open the chanel and keep the chanel connected to the server without having to refresh the page. First we will initialize the project chanel with syntax
1 2 | rails g channel notifications |
1 2 3 4 5 | create app/channels/notifications_channel.rb identical app/javascript/channels/index.js identical app/javascript/channels/consumer.js create app/javascript/channels/notifications_channel.js |
- Rails will automatically create two more files for us,
app/channels/notifications_channel.rb
andapp/javascript/channels/notifications_channel.js
Step 4
- Set the connetion on the server side
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # app/channels/application_cable/connection.rb module ApplicationCable class Connection < ActionCable::Connection::Base identified_by :current_user def connect self.current_user = find_verified_user end private def find_verfied_user if user_id = cookies.signed[:user_id] || request.session[:user_id] User.find_by(id: user_id) || reject_unauthorized_connection end end |
Step 5
- Set up channels on server side as follows:
1 2 3 4 5 6 7 8 9 10 11 | # app/channels/notifications_channel.rb class NotificationsChannel < ApplicationCable::Channel def subscribed stream_from "notifications:#{current_user.id}" end def unsubscribed stop_all_streams end end |
Step 6
- Establish a connection on the client side
1 2 3 | #config/routes.rb mount ActionCable.server => '/cable' |
Step 7
- Set up Subscribers on the client side
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #app/assets/javascripts/channels/notifications.js App.notifications = App.cable.subscriptions.create("NotificationsChannel", { connected: function() { // Called when the subscription is ready for use on the server }, disconnected: function() { // Called when the subscription has been terminated by the server }, received: function(data) { // Called when there's incoming data on the websocket for this channel $("#notification-list").prepend(data.layout) } }); |
Step 8
- Create a job to perform the response for the client, and the broadcast will call the channel.
1 2 | rails g job NotificationBroadcastJob |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class NotificationBroadcastJob < ApplicationJob queue_as :default def perform notification ActionCable.server.broadcast "notifications:#{notification.receiver_id}", counter: render_counter(notification.receiver.notifications.count), layout: render_notification(notification) end private def render_counter counter ApplicationController.renderer.render(partial: "notifications/counter", locals: {counter: counter}) end def render_notification notification ApplicationController.renderer.render(partial: "notifications/notification", locals: {notification: notification}) end end |
Step 9
- Finally, we will use Active Record’s Callbacks to call Jobs to perform the response to the client.
1 2 3 4 5 6 | after_create :send_notification def send_notification NotificationBroadcastJob.perform_now(self) end |
summary
So we have completed the notification creation in the application, I am a new rails learner so the article has many shortcomings, hope everyone can comment to be able to improve.