+1

Rails Action Cable

Giới thiệu

Realtime được hiểu đơn giản là thời gian thực, ví dụ như trong ứng dụng chat, khi bạn gõ tin nhắn trên máy của bạn thì nó hiện trực tiếp lên trên màn hình của người cùng chat. Thời gian thực ở đây nghĩa là thời gian chênh lệch có độ trễ nhỏ, tính bằng mili giây nên không đáng kể. Action Cable là một tính năng tích hợp WebSocket của Rails. nó cho phép bạn xây dựng được các tính năng Realtime.

Thực hiện

New app

Giờ ta sẽ thử tạo mới một project demo để gửi thông báo realtime đến User.

rails new demo_action_cable -d mysql

Ở đây mình dùng gem "devise" để xác thực User, bạn có thể tham khảo thêm ở đây devise

Connections

Tạo kết nối giữa Client-Server, hi server chấp nhận websocket thì một đối tượng connection sẽ được khởi tạo.

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = User.find_by id: cookies.signed[:user_id]

      reject_unauthorized_connection unless current_admin.present?
    end
  end
end

Channel

Đăng ký channel, các thông báo sau khi tạo mới sẽ được chuyển đến các kênh này, vì là thông báo cá nhân mỗi user có một channel, ta có thể dựa vào current_user.id để phân biệt các channel này. Method subscriebed ở đây để thực hiện việc đăng ký channel để trao đổi dữ liệu với client

class NotificationChannel < ApplicationCable::Channel
  def subscribed
    stream_from "user_notification_#{current_user.id}_channel"
  end
end

Sau khi đã đăng ký channel thì ta bước tiếp theo là code để tính năng hoạt động.

Tiếp theo ta viết 1 service để gửi thông báo đến User sau khi thông báo được tạo.

class PushNotificationService
  def initialize args = {}
    @notification = args[:notification]
    @current_user = notification.user
  end

  def perform
    ActionCable.server.broadcast "user_notification_#{notification.user.id}_channel", notification_attrs
  rescue StandardError
    nil
  end

  private
  attr_reader :notification, :current_user

  def render_notification
    ApplicationController.renderer.render partial: partial_html_path, locals: {notification: notification}
  end

  def notification_attrs
    {
      id: notification.id,
      message: render_notification,
      count: @current_user.notifications.seen(false).size,
      url: notification.display_url
    }
  end

  def partial_html_path
    "user/notifications/notification"
  end
end

model/notification.rb dùng callback để sau mỗi lần thông báo được tạo sẽ gửi đến cho User.

after_create :push_notify

private

  def push_notify_to_user
     PushNotificationService.new(notification: self).perform
  end

Tiếp theo ở views:
để hiển thị nội dung thông báo. views/user/notifications/_notification

<li>
  <%= notification.content %>
</li>

views/user/notifications/_list_notification.html.slim

<ul class="js-list-notifications">
  <%= render partial: "notification", collection: notifications, as: :notification %>
</ul>

Thêm vào js file channels/user/notification
> Tạo kết nối với channel mà ta muốn. Ở đây là NotificationChannel. Dữ liệu nhận về sẽ thông qua biến data ở hàm received.

$(function() {
  App.notifications = App.cable.subscriptions.create({
      channel: 'NotificationChannel'
    },
    {
      connected: function() {},
      disconnected: function() {},
      received: function(data) {
        $('ul#js-list-notifications').prepend(data.message);
        $('.js-notification-size').html(data.count);
      }
    });
});

Trong routes:

mount ActionCable.server => "/cable"

application.js

//= require ./channels/user/notification
//= require cable

Nguồn

https://guides.rubyonrails.org/action_cable_overview.html
https://blog.heroku.com/real_time_rails_implementing_websockets_in_rails_5_with_action_cable


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.