0
0

ActionCableでリアルタイム通信の実装

Last updated at Posted at 2023-11-16

始めに

Websocketとは

ウェブブラウザとサーバー間で双方向のリアルタイム通信を可能にする技術。通常のHTTPリクエストと異なり、一度コネクション(※)を確立すると、そのコネクションを維持したままデータをやり取りできる

コネクションとは、クライアント(通常はウェブブラウザ)とサーバー間で確立された通信路やセッションを指す。

通常のHTTPリクエスト
クライアントがサーバーにリクエストを送り、サーバーがレスポンスを返すという単発のやりとり。

Action Cableとは

RubyonRailsフレームワークに統合された、WebSocketベースの双方向通信ライブラリ。これにより、Railsアプリケーション内でのリアルタイムな双方向通信が簡単に実現できる。

イメージ図
Image from Gyazo

コマンド実行後作成されるファイル
rails g channel room #チャネル名
# 実行結果
Running via Spring preloader in process *****
      invoke  test_unit
      create  test/channels/room_channel_test.rb
      create  app/channels/room_channel.rb    #サーバー側の処理
   identical  app/javascript/channels/index.js
   identical  app/javascript/channels/consumer.js
      create  app/javascript/channels/room_channel.js  #クライアント側の処理

設定の仕方はいろいろ記事がありました
ありがとうございます。

参考

https://techtechmedia.com/action-cable-rails6/
https://qiita.com/rhiroe/items/4c4e983e34a44c5ace27


実装

投稿の内容ごとにチャンネルを作成してみました。

Image from Gyazo
Image from Gyazo

一部を抜粋したものです。細かい設定は参考URLにわかりやすく記述されています。

投稿内容からチャンネルへ遷移
# <省略>
<%= link_to channel_post_path(post), data: { turbo: false }, class: "block h-full p-6 flex items-center gap-x-3" do %>
  <span class="font-semibold text-brack dark:text-gray-200">
      <%= post.content %>    
  </span>       
 <% end %>
posts/cahnnel.html.erb
<div class="channel bg-green-50" style="background-image: url(<%= asset_path('channel_04.png') %>)">
   <div class="flex justify-center">
       <form class="mt-6">
           <%= label_tag :content, '', class: 'block text-gray-700' %>
           <input type="text" data-behavior="room_speaker" class="py-2 px-3 border border-gray-300 rounded-lg shadow-sm focus:ring focus:ring-blue-200 focus:outline-none" placeholder="メッセージ入力">
       </form>
   </div>
   <div class="flex-1 p:2 sm:p-6 justify-between flex flex-col h-screen">
       <div id='messages' data-room_id="<%= @room.id %>" class="flex flex-col space-y-4 p-3 overflow-y-auto scrollbar-thumb-blue scrollbar-thumb-rounded scrollbar-track-blue-lighter scrollbar-w-2 scrolling-touch">
           <%= render @messages %>
       </div>
   </div> 
</div>
posts_controller.rb
# <省略>
def channel
    @room = Post.find(params[:id])
    @messages = @room.messages
  end
channels/room_channel.rb
class RoomChannel < ApplicationCable::Channel
  def subscribed
    stream_from "room_#{params['room_id']}" #引数として特定のチャンネル名を指定.これにより、クライアントがこのチャンネルにサブスクライブ(購読)すると、そのクライアントはこのチャンネルに関連付けられたストリームを受け取ることができる
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end

  def speak(data)
    Message.create!(content: data['message'], user_id: current_user.id, post_id: data['room_id'], created_at: Time.now)
  end

end
javascript/room_channel.js
import consumer from "./consumer"
import $ from 'jquery';

$(function(){
	const chatChannel = consumer.subscriptions.create({ channel: 'RoomChannel', room_id: $('#messages').data('room_id') }, {
		connected() {
		},

		disconnected() {
			// Called when the subscription has been terminated by the server
		},

		received(data) {
			const messagesContainer = $('#messages');
			messagesContainer.prepend(data['message']);
		},

		speak: function(message) {
			return this.perform('speak', {message: message, room_id: $('#messages').data('room_id')});
		}
	});

	$(document).on('keypress', '[data-behavior~=room_speaker]', function(event) {
		if (event.key === 'Enter') {
			chatChannel.speak(event.target.value);
			event.target.value = '';
			return event.preventDefault();
		}
	});
});
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0