スタック・オーバーフロー Asked by takumi mori on November 15, 2021
Rails5のaction cableを使用して、チャット機能を作成しています。
一つのアカウントが複数のルームに出入りし、チャットしあえるようにしたいのですが、ルームに入るタイミングや、ページのリロードなどによって、メッセージが出たり出なかったりします。
その原因が何故なのかわかりません。
ログイン後、自分のアカウントに紐づいたルームの名前が表示されます。
ルームを選択するとチャット画面に遷移します。
以下はそれぞれ別々のアカウントで同じルームに入った状態の画像です。
左からルームに入り、次に右が入り、左がメッセージを送ると、双方に表示されます。
しかし、例えばこの状態で左だけ画面をリロードし、再度メッセージを入力すると、右に表示されません。
他にも、
・どちらから先にルームに入るか
・どちらから先に送るか
・どちらがリロードするか
などによって、メッセージが送られる場合とそうでない場合があります。
それがどうしてなのかわかりません。
以下、どのようなパターンの時に表示されるか、されないかを表にまとめています。
Aは左側、Bは右側の画面です
例えば上の表の3行目は、「Aからルームに入りBも入った後、Aから送った場合はBにメッセージが出たが、その後Bで画面をリロードし、Aから送った場合は出ない」という意味です
下の表の1行目は、「Aからルームに入りメッセージを送った後に、Bもルームに入り、そこでAからメッセージを送ると表示されたが、その後Aのリロードし、Aからメッセージを送ると、Bに表示されない」という意味です
application.js
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
// vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require rails-ujs
//= require jquery
//= require bootstrap
//= require turbolinks
//= require jquery3
//= require popper
//= require bootstrap
cable.js
// Action Cable provides the framework to deal with WebSockets in Rails.
// You can generate new channels where WebSocket features live using the `rails generate channel` command.
//
//= require action_cable
//= require_self
//= require_tree ./channels
(function() {
this.App || (this.App = {});
App.cable = ActionCable.createConsumer();
}).call(this);
room_channel.js
// $(function() {}; で囲むことでレンダリング後に実行される
// レンダリング前に実行されると $('#messages').data('room_id') が取得できない
// $(window).on('load', function() {
$(function() {
var channel = 'RoomChannel';
var room = $('#messages').attr('data-room_id');
chatChannel = App.cable.subscriptions.create({ channel: channel, room: room}, {
connected() {
// Called when the subscription is ready for use on the server
},
disconnected() {
// Called when the subscription has been terminated by the server
},
received: function(data) {
return $('#messages').append(data['message']);
},
speak: function(message) {
return this.perform('speak', {
message: message
});
}
});
$(document).on('keypress', '[data-behavior~=room_speaker]', function(event) {
if (event.keyCode === 13) {
chatChannel.speak(event.target.value);
event.target.value = '';
return event.preventDefault();
}
});
});
assets.rb
# Be sure to restart your server when you modify this file.
# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = '1.0'
# Add additional assets to the asset load path.
# Rails.application.config.assets.paths << Emoji.images_path
# Add Yarn node_modules folder to the asset load path.
Rails.application.config.assets.paths << Rails.root.join('node_modules')
# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in the app/assets
# folder are already added.
# Rails.application.config.assets.precompile += %w( admin.js admin.css )
Rails.application.config.assets.precompile += %w( cable.js )
routes.rb
Rails.application.routes.draw do
~省略~
# チャット用ルーティング
mount ActionCable.server => '/cable'
devise_for :applicants
get '/chat', to:'rooms#index'
# resourcesを使うとRESTfulなURLを自動生成できる
resources :rooms, only: %i[show], param: :id_digest
end
room_channel.rb
class RoomChannel < ApplicationCable::Channel
def subscribed
2.times { puts '***test***' }
stream_from "room_channel_#{params[:room]}"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
def speak(data)
# jsで実行されたspeakのmessageを受け取り、room_channelのreceivedにブロードキャストする
# ActionCable.server.broadcast 'room_channel', message: data['message']
Message.create! content: data['message'], applicant_id: current_user.id, room_id: params[:room]
end
end
index.html.erb
<div>
<ul>
<% @participations.each do |p| %>
<li><%= link_to p.room.name, room_path(p.room.id_digest), data: {"turbolinks" => false} %></li>
<% end %>
</ul>
</div>
show.html.erb
<%#ここの data-room_id を使ってjs側で部屋を見分ける %>
<div id='messages' data-room_id="<%= @room.id %>">
<%= render @messages %>
</div>
<%= label_tag :content, 'Say something:' %>
<%= text_field_tag :content, nil, data: { behavior: 'room_speaker' } %>
<%= javascript_include_tag 'cable.js' %>
message.rb
class Message < ApplicationRecord
validates :content, presence: true
# createの後にコミットする { MessageBroadcastJobのperformを遅延実行 引数はself }
after_create_commit { MessageBroadcastJob.perform_later self }
belongs_to :applicant
belongs_to :room
end
message_broadcast_job.rb
class MessageBroadcastJob < ApplicationJob
queue_as :default
def perform(message)
ActionCable.server.broadcast "room_channel_#{message.room_id}", message: render_message(message)
end
private
def render_message(message)
ApplicationController.renderer.render partial: 'messages/message', locals: { message: message }
end
end
Started GET "/cable" for 127.0.0.1 at 2020-07-18 17:06:46 +0900
Started GET "/cable/" [WebSocket] for 127.0.0.1 at 2020-07-18 17:06:46 +0900
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
Applicant Load (0.2ms) SELECT "applicants".* FROM "applicants" WHERE "applicants"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Registered connection (Z2lkOi8vbWF0Y2hpbmctYXBwL0FwcGxpY2FudC8x)
***test***
***test***
RoomChannel is transmitting the subscription confirmation
RoomChannel is streaming from room_channel_6
RoomChannel#speak({"message"=>"テスト"})
(0.1ms) begin transaction
Applicant Load (0.5ms) SELECT "applicants".* FROM "applicants" WHERE "applicants"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
Room Load (0.1ms) SELECT "rooms".* FROM "rooms" WHERE "rooms"."id" = ? LIMIT ? [["id", 6], ["LIMIT", 1]]
SQL (0.4ms) INSERT INTO "messages" ("content", "created_at", "updated_at", "applicant_id", "room_id") VALUES (?, ?, ?, ?, ?) [["content", "テスト"], ["created_at", "2020-07-18 08:06:52.345133"], ["updated_at", "2020-07-18 08:06:52.345133"], ["applicant_id", 2], ["room_id", 6]]
(2.5ms) commit transaction
[ActiveJob] Enqueued MessageBroadcastJob (Job ID: 813258ad-3d30-4c46-90ec-d24f4a83e2ff) to Async(default) with arguments: #<GlobalID:0x00007f9d4b4cee98 @uri=#<URI::GID gid://matching-app/Message/345>>
Message Load (0.3ms) SELECT "messages".* FROM "messages" WHERE "messages"."id" = ? LIMIT ? [["id", 345], ["LIMIT", 1]]
[ActiveJob] [MessageBroadcastJob] [813258ad-3d30-4c46-90ec-d24f4a83e2ff] Performing MessageBroadcastJob (Job ID: 813258ad-3d30-4c46-90ec-d24f4a83e2ff) from Async(default) with arguments: #<GlobalID:0x00007f9d4fac3048 @uri=#<URI::GID gid://matching-app/Message/345>>
[ActiveJob] [MessageBroadcastJob] [813258ad-3d30-4c46-90ec-d24f4a83e2ff] Applicant Load (0.1ms) SELECT "applicants".* FROM "applicants" WHERE "applicants"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
[ActiveJob] [MessageBroadcastJob] [813258ad-3d30-4c46-90ec-d24f4a83e2ff] Rendered messages/_message.html.erb (1.5ms)
[ActiveJob] [MessageBroadcastJob] [813258ad-3d30-4c46-90ec-d24f4a83e2ff] [ActionCable] Broadcasting to room_channel_6: {:message=>"<div class='message'>n <p>テスト2: テスト</p>n <p>2020-07-18 08:06:52 UTC</p>n</div>"}
[ActiveJob] [MessageBroadcastJob] [813258ad-3d30-4c46-90ec-d24f4a83e2ff] Performed MessageBroadcastJob (Job ID: 813258ad-3d30-4c46-90ec-d24f4a83e2ff) from Async(default) in 6.85ms
RoomChannel transmitting {"message"=>"<div class='message'>n <p>テスト2: テスト</p>n <p>2020-07-18 08:06:52 UTC</p>n</div>"} (via streamed from room_channel_6)
RoomChannel transmitting {"message"=>"<div class='message'>n <p>テスト2: テスト</p>n <p>2020-07-18 08:06:52 UTC</p>n</div>"} (via streamed from room_channel_6)
RoomChannel#speak({"message"=>"テストです"})
(0.1ms) begin transaction
Applicant Load (0.2ms) SELECT "applicants".* FROM "applicants" WHERE "applicants"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Room Load (0.1ms) SELECT "rooms".* FROM "rooms" WHERE "rooms"."id" = ? LIMIT ? [["id", 6], ["LIMIT", 1]]
SQL (1.0ms) INSERT INTO "messages" ("content", "created_at", "updated_at", "applicant_id", "room_id") VALUES (?, ?, ?, ?, ?) [["content", "テストです"], ["created_at", "2020-07-18 08:07:38.239354"], ["updated_at", "2020-07-18 08:07:38.239354"], ["applicant_id", 1], ["room_id", 6]]
(3.0ms) commit transaction
[ActiveJob] Enqueued MessageBroadcastJob (Job ID: e6707164-e702-4d10-87ff-720ce3b2b22e) to Async(default) with arguments: #<GlobalID:0x00007f9d4cc4bfa8 @uri=#<URI::GID gid://matching-app/Message/346>>
Message Load (0.3ms) SELECT "messages".* FROM "messages" WHERE "messages"."id" = ? LIMIT ? [["id", 346], ["LIMIT", 1]]
[ActiveJob] [MessageBroadcastJob] [e6707164-e702-4d10-87ff-720ce3b2b22e] Performing MessageBroadcastJob (Job ID: e6707164-e702-4d10-87ff-720ce3b2b22e) from Async(default) with arguments: #<GlobalID:0x00007f9d4f87a6d8 @uri=#<URI::GID gid://matching-app/Message/346>>
[ActiveJob] [MessageBroadcastJob] [e6707164-e702-4d10-87ff-720ce3b2b22e] Applicant Load (0.2ms) SELECT "applicants".* FROM "applicants" WHERE "applicants"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
[ActiveJob] [MessageBroadcastJob] [e6707164-e702-4d10-87ff-720ce3b2b22e] Rendered messages/_message.html.erb (3.2ms)
[ActiveJob] [MessageBroadcastJob] [e6707164-e702-4d10-87ff-720ce3b2b22e] [ActionCable] Broadcasting to room_channel_6: {:message=>"<div class='message'>n <p>テスト1: テストです</p>n <p>2020-07-18 08:07:38 UTC</p>n</div>"}
[ActiveJob] [MessageBroadcastJob] [e6707164-e702-4d10-87ff-720ce3b2b22e] Performed MessageBroadcastJob (Job ID: e6707164-e702-4d10-87ff-720ce3b2b22e) from Async(default) in 11.23ms
RoomChannel transmitting {"message"=>"<div class='message'>n <p>テスト1: テストです</p>n <p>2020-07-18 08:07:38 UTC</p>n</div>"} (via streamed from room_channel_6)
RoomChannel transmitting {"message"=>"<div class='message'>n <p>テスト1: テストです</p>n <p>2020-07-18 08:07:38 UTC</p>n</div>"} (via streamed from room_channel_6)
Started GET "/rooms/e7f6c011776e8db7cd330b54174fd76f7d0216b612387a5ffcfb81e6f0919683" for 127.0.0.1 at 2020-07-18 17:07:40 +0900
Processing by RoomsController#show as HTML
Parameters: {"id_digest"=>"e7f6c011776e8db7cd330b54174fd76f7d0216b612387a5ffcfb81e6f0919683"}
Applicant Load (0.4ms) SELECT "applicants".* FROM "applicants" WHERE "applicants"."id" = ? ORDER BY "applicants"."id" ASC LIMIT ? [["id", 1], ["LIMIT", 1]]
Room Load (0.4ms) SELECT "rooms".* FROM "rooms" WHERE "rooms"."id_digest" = ? LIMIT ? [["id_digest", "e7f6c011776e8db7cd330b54174fd76f7d0216b612387a5ffcfb81e6f0919683"], ["LIMIT", 1]]
Rendering rooms/show.html.erb within layouts/application
Message Load (5.3ms) SELECT "messages".* FROM "messages" WHERE "messages"."room_id" = ? [["room_id", 6]]
Applicant Load (0.2ms) SELECT "applicants".* FROM "applicants" WHERE "applicants"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
CACHE Applicant Load (0.0ms) SELECT "applicants".* FROM "applicants" WHERE "applicants"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
CACHE Applicant Load (0.0ms) SELECT "applicants".* FROM "applicants" WHERE "applicants"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
Applicant Load (0.4ms) SELECT "applicants".* FROM "applicants" WHERE "applicants"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
CACHE Applicant Load (0.0ms) SELECT "applicants".* FROM "applicants" WHERE "applicants"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
CACHE Applicant Load (0.0ms) SELECT "applicants".* FROM "applicants" WHERE "applicants"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
~省略~
CACHE Applicant Load (0.0ms) SELECT "applicants".* FROM "applicants" WHERE "applicants"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
CACHE Applicant Load (0.0ms) SELECT "applicants".* FROM "applicants" WHERE "applicants"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Rendered collection of messages/_message.html.erb [88 times] (242.0ms)
Rendered rooms/show.html.erb within layouts/application (291.9ms)
Rendered layouts/_rails_default.erb (44.2ms)
Rendered layouts/_shim.html.erb (0.7ms)
Rendered layouts/_header.html.erb (1.6ms)
Rendered layouts/_footer.html.erb (0.7ms)
Completed 200 OK in 359ms (Views: 334.2ms | ActiveRecord: 19.2ms)
Finished "/cable/" [WebSocket] for 127.0.0.1 at 2020-07-18 17:07:41 +0900
RoomChannel stopped streaming from room_channel_6
Started GET "/cable" for 127.0.0.1 at 2020-07-18 17:07:41 +0900
Started GET "/cable/" [WebSocket] for 127.0.0.1 at 2020-07-18 17:07:41 +0900
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
Applicant Load (0.2ms) SELECT "applicants".* FROM "applicants" WHERE "applicants"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Registered connection (Z2lkOi8vbWF0Y2hpbmctYXBwL0FwcGxpY2FudC8x)
***test***
***test***
RoomChannel is transmitting the subscription confirmation
RoomChannel is streaming from room_channel_6
RoomChannel#speak({"message"=>"あああ"})
(0.1ms) begin transaction
Applicant Load (0.3ms) SELECT "applicants".* FROM "applicants" WHERE "applicants"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Room Load (0.1ms) SELECT "rooms".* FROM "rooms" WHERE "rooms"."id" = ? LIMIT ? [["id", 6], ["LIMIT", 1]]
SQL (0.7ms) INSERT INTO "messages" ("content", "created_at", "updated_at", "applicant_id", "room_id") VALUES (?, ?, ?, ?, ?) [["content", "あああ"], ["created_at", "2020-07-18 08:07:46.724639"], ["updated_at", "2020-07-18 08:07:46.724639"], ["applicant_id", 1], ["room_id", 6]]
(2.6ms) commit transaction
[ActiveJob] Enqueued MessageBroadcastJob (Job ID: 669bd380-4fef-47c4-a968-00543c534abc) to Async(default) with arguments: #<GlobalID:0x00007f9d5095ea30 @uri=#<URI::GID gid://matching-app/Message/347>>
Message Load (0.6ms) SELECT "messages".* FROM "messages" WHERE "messages"."id" = ? LIMIT ? [["id", 347], ["LIMIT", 1]]
[ActiveJob] [MessageBroadcastJob] [669bd380-4fef-47c4-a968-00543c534abc] Performing MessageBroadcastJob (Job ID: 669bd380-4fef-47c4-a968-00543c534abc) from Async(default) with arguments: #<GlobalID:0x00007f9d4fb298e8 @uri=#<URI::GID gid://matching-app/Message/347>>
[ActiveJob] [MessageBroadcastJob] [669bd380-4fef-47c4-a968-00543c534abc] Applicant Load (0.2ms) SELECT "applicants".* FROM "applicants" WHERE "applicants"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
[ActiveJob] [MessageBroadcastJob] [669bd380-4fef-47c4-a968-00543c534abc] Rendered messages/_message.html.erb (7.1ms)
[ActiveJob] [MessageBroadcastJob] [669bd380-4fef-47c4-a968-00543c534abc] [ActionCable] Broadcasting to room_channel_6: {:message=>"<div class='message'>n <p>テスト1: あああ</p>n <p>2020-07-18 08:07:46 UTC</p>n</div>"}
[ActiveJob] [MessageBroadcastJob] [669bd380-4fef-47c4-a968-00543c534abc] Performed MessageBroadcastJob (Job ID: 669bd380-4fef-47c4-a968-00543c534abc) from Async(default) in 23.72ms
RoomChannel transmitting {"message"=>"<div class='message'>n <p>テスト1: あああ</p>n <p>2020-07-18 08:07:46 UTC</p>n</div>"} (via streamed from room_channel_6)
Finished "/cable/" [WebSocket] for 127.0.0.1 at 2020-07-18 17:12:11 +0900
RoomChannel stopped streaming from room_channel_6
どうしてメッセージが出る時と出ない時があるのか知りたいです。
同じ「room_channel_6」に接続しているように見えるのですが・・・
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP