Online/offline appearance realtime với Actioncable trong Rails 5

Introduction

Actioncable là tính năng mới của Rails 5 nhờ sử dụng websocket để tạo realtime application một cách đễ dàng và nhanh chống, cho phép server và client tương tác với nhau realtime. Hôm này mình sẽ đưa ra một ví dụ về cách sử dụng actioncable để tạo Online/offline appearance cho người dùng hệ thống. Về phần đầu tiên để config, tạo channel hoặc cách làm việc của actioncable thế nào mình sẽ không nói ở đây nữa. Nếu bạn chưa làm quên với nó, có thể tham khảo một số tài liệu sau: https://github.com/rails/rails/tree/master/actioncable https://blog.heroku.com/real_time_rails_implementing_websockets_in_rails_5_with_action_cable

Let's start!

  • Tạo migration them column online:boolean mới vào bảng user (hoặc bảng khác tùy thuộc vào project của bạn). Trường này dùng để lưu appearance của user để biết là đang online hay không.
class AddOnlineToUser < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :online, :boolean, default: :false
  end
end

chạy migrate:

rake db:migrate
  • Tạo list user cùng với status online/offline:
# app/controllers/rooms_controller.rb

  def show
    @users = User.page(params[:page]).per 10
  end
<ul id="users-list">
  <% @users.each do |member| %>
      <li>
        <%= member.name %>
        <% if member.online %>
          <%= image_tag "green_dot.png", id: "#{member.user_id}-status", class: "active" %>
        <% else %>
          <%= image_tag "green_dot.png", id: "#{member.user_id}-status", class: "inactive" %>
        <% end %>
      </li>
  <% end %>
</ul>

Styles css:

/* app/assets/stylesheets/main.css */ 
.active { display: ; } .inactive { display: none; }
  • Tạo appearance channel: Khi user join vào hệ thống (subscribe) thì mình sẽ update online: true. Khi user ra khỏi hệ thống (unsubscribe) thì mình sẽ update online: false.
rails g channel appearance

Channel sẽ có như sau:

#app/channels/appearance_channel.rb
class AppearanceChannel < ApplicationCable::Channel
  def subscribed
    member = User.where(user_id: current_user.id).first
    return unless member
    member.update_attributes(online: true)
    stream_from "appearance_user"
  end

  def unsubscribed
    member = User.where(user_id: current_user.id).first
    return unless member
    member.update_attributes(online: false)
  end
end
  • Tạo job để publish data: sau khi user thay đổi status online thành true/false, chúng ta sẽ publish data tới client để cập nhật lại hình ảnh active (green dot) hoặc không active (không hiện green dot).
rails g job AppearanceBroadcast
#app/jobs/appearance_broadcast_job.rb
class AppearanceBroadcastJob < ApplicationJob
  queue_as :default

  def perform(user)
    ActionCable.server.broadcast "appearance_user", render_json(user)
  end

  private

  def render_json(user)
    ApplicationController.renderer.render(json: user)
  end

end
# app/models/user.rb add this right under the validations 
after_update_commit {AppearanceBroadcastJob.perform_later self}

Sau khi server đã publish data, bên client sẽ nhận được đã data đó và phải xử lý cập nhật lại hình ảnh active (green dot) hoặc không active (không hiện green dot).

// app/assets/javascripts/channels/appearance.js
App.appearance = App.cable.subscriptions.create({
  channel:'AppearanceChannel'
 }, {
  received: function(data) {
    var user = JSON.parse(data)
    if (user.online === true){
      $(userImgIdConstructor(user)).attr('class', 'active');
    };
    if (user.online === false){
      $(userImgIdConstructor(user)).attr('class', 'inactive');
    };
  }
});

var userImgIdConstructor = function(user){
  return "#" + user.user_id + "-status";
}

Conclusion

Done! Bây giờ chúng ta đã làm xong về việc tạo online/offline status cho hệ thông của mình. Bạn thấy rằng nhờ sử dụng actioncable thì việc tạo realtime application không phải là vấn đề khó khăn nữa.