質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Action Cable

Action Cableは、WebSocketをRailsに組み込む機能。Rails4でオプションとして存在していたWebSocketをRails5で標準機能したものです。Railsアプリケーションと同様のスタイルで、Rubyを用いたリアルタイム機能を記述できます。

Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

WebSocket

WebSocketとは双方向・全二重コミュニケーションのためのAPIでありプロトコルのことを指します。WebSocketはHTML5に密接に結びついており、多くのウェブブラウザの最新版に導入されています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

0回答

972閲覧

rails5のaction cableでメッセージが同期されるときと同期されない時がある

takumimori

総合スコア10

Action Cable

Action Cableは、WebSocketをRailsに組み込む機能。Rails4でオプションとして存在していたWebSocketをRails5で標準機能したものです。Railsアプリケーションと同様のスタイルで、Rubyを用いたリアルタイム機能を記述できます。

Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

WebSocket

WebSocketとは双方向・全二重コミュニケーションのためのAPIでありプロトコルのことを指します。WebSocketはHTML5に密接に結びついており、多くのウェブブラウザの最新版に導入されています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

0グッド

0クリップ

投稿2020/07/18 09:39

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

どうしてメッセージが出る時と出ない時があるのか知りたいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

pecchan

2020/07/28 07:11

私も同様の現象出てるかもです。 エラーログ確認されましたか?&ログ追記されたら回答しやすいかもです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問