概要
チャットルームのように、特定の部屋に入っているユーザにのみメッセージを送受信しようと、WebScocket-railsのチャネル機能を利用しようとしてハマったのでメモ。
ruby 2.2.2
rails 4.2.6
websocket-rails 0.7.0 712fd4e
結論
タイトルおよび、下記参考URLに記載されている通り、チャネルに対してtriggerしても、サーバ側でイベントルーティングされない。
このため、trigger(javascript ⇒ Railsの送信)は、チャネルではなく大元のディスパッチャに対して行い、bind(Rails ⇒ javascriptの送信)は、チャネル毎に行うのが良いと思われる。
詳細
特定のチャネルに接続しているクライアントに対してのみデータを送受信するには、以下のchannerl_dispatcherように、特定のチャネル名を指定したディスパッチャを作成する。
ws_dispatcher = new WebSocketRails("localhost:3000/websocket");
channel_dispatcher = ws_dispatcher.subscribe(room_name);
ただし、
ws_dispatcher = new WebSocketRails("localhost:3000/websocket");
channel_dispatcher = ws_dispatcher.subscribe(room_name);
channel_dispatcher.trigger("new_message", {msg_body: comment});
channel_dispatcher.bind("new_message", function(message){
// 以下省略
などとした場合、events.rbにはnew_messageイベントは入ってこなかった。
この場合でも、複数のブラウザ間でルーム内に限定したメッセージの送受信は実際に動作していたため、events.rbを通らずともjavascript間でtrigger ⇒ bindできているようであった。
(あくまでもチャットは動いているため、events.rb⇒チャット用コントローラを経由しているはずと思い込んでしまう可能性があるので注意)
websocket-railsのwikiによると、chanelを利用した場合は、イベントルーティングの対象外となるが仕様のようだ。
結局、triggerはチャネルを利用せず、メッセージにチャットルームを表す識別子を含めた上で大元のディスパッチャを経由してサーバへ送信、コントローラ内でチャットルームを区別して、ルーム毎のチャネルに対してcastするようにするのが良いと思われる。
subscribe :new_message, to: WebsocketChatController, with_method: :new_message
def new_message
# クライアントからのメッセージを取得
recieve_message = message()
WebsocketRails[recieve_message[:room_name]].trigger(:cast_new_message, recieve_message)
end
WebSocketRails("localhost:3000/websocket");
channel_dispatcher = ws_dispatcher.subscribe(room_name);
// メッセージ送信時の処理
$('#send').click(function(){
// room_name = document.getElementById("room_name").innerText;
comment = $("#comment").prop("value");
ws_dispatcher.trigger("new_message", {room_name: room_name, user_name: user_name, msg_body: comment});
});
// メッセージ受信時の処理
channel_dispatcher.bind("cast_new_message", function(message){
message_li = document.createElement("li");
message_li.textContent = message.room_name + " | " + message.user_name + " | " + message.msg_body;
document.getElementById("chat_area").appendChild(message_li);
});
(上記では、大元のディスパッチャに送るか、チャネル毎のディスパッチャに送るかで、"new_message"と、"cast_new_message"とにイベント名を分けているが、判りやすくするためそうしているだけで、同じイベント名でも問題はないはず)
参考
[websocket-rails wiki :Working with Channels]
感謝