solid cableを使ったwebsocket通信をturboフレームなしで実装してみました。
使用しなかった理由は、自分のプロダクトのバージョンとturbo-railsやimportmapが合わず、不具合が発生したからです。
solid cableのみでのwebsocketの実装はあまり記事などなかったので簡単にまとめておこうかなと思いました。solid cableのinstallは完了しているものとしておきます。
間違いや粗い部分があるかもしれません。🙇
実装フロー
- application(server)側でchannelを作成する
- client(webブラウザ)側でsubscribeするコードを書く
- 任意にbroadcastする
- js側で必要な処理(appendなど)を実装する
rails g channel Message
とすると下記が作成されます。
app/channels/message_channel.rb
app/channels/application_cable/channel.rb
app/channels/application_cable/connection.rb
test/channels/message_channel_test.rb
sprocketの場合は下記ファイルも必要となります。私は自動追加されなかったので手動で作成しました。
app/assets/javascripts/channels/consumer.js
app/assets/javascripts/channels/index.js
app/assets/javascripts/channels/message_channel.js
それぞれの中身はこちらです。connection.rbとchannel.rbはそのままにしています。
app/channels/message_channel.rb
# app/channels/message_channel.rb
class MessageChannel < ApplicationCable::Channel
def subscribed
# クライアントがこのチャネルを購読したときに実行されます。
# ここで、特定のストリーム(例: "message_channel")からのメッセージを購読します。
# stream_from は、ActionCable.server.broadcast で送信されたメッセージを受け取るために必要です。
stream_from "message_channel"
logger.debug "[MessageChannel] Subscribed to message_channel"
logger.debug "[MessageChannel] Current connection ID: #{connection.connection_identifier}"
# サブスクライブ時にテストメッセージを送信
test_broadcast
end
def unsubscribed
# チャネルの購読が解除されたときに実行されます。
# 必要に応じて、ここでクリーンアップ処理を行います。
logger.debug "[MessageChannel] Unsubscribed from message_channel"
logger.debug "[MessageChannel] Connection ID: #{connection.connection_identifier}"
end
# クライアントからメッセージを受信するためのメソッド
# クライアント側の JavaScript から `perform("receive", data)` のように呼び出されます。
def receive(data)
logger.debug "[MessageChannel] Received message: #{data.inspect}"
# 受信したデータをそのまま、このチャネルを購読している全てのクライアントにブロードキャストします。
# これにより、メッセージを送ったクライアント自身も、他の購読者も、そのメッセージを受け取ります。
ActionCable.server.broadcast "message_channel", data
logger.debug "[MessageChannel] Broadcasted message: #{data.inspect}"
end
private
def test_broadcast
logger.debug "[MessageChannel] Attempting test broadcast..."
begin
ActionCable.server.broadcast(
"message_channel",
{ message: "Test broadcast", timestamp: Time.current }
)
logger.debug "[MessageChannel] Test broadcast successful"
rescue => e
logger.error "[MessageChannel] Test broadcast failed: #{e.message}"
logger.error e.backtrace.join("\n")
end
end
end
app/assets/javascripts/channels/consumer.js
(function() {
this.App || (this.App = {});
App.cable = ActionCable.createConsumer();
}).call(this);
app/assets/javascripts/channels/index.js
App = {};
App.cable = ActionCable.createConsumer();
app/assets/javascripts/channels/message_channel.js
App.messageChannel = App.cable.subscriptions.create("MessageChannel", {
connected: function() {
console.log("Connected to MessageChannel");
},
disconnected: function() {
console.log("Disconnected from MessageChannel");
},
received: function(data) {
console.log("Received message:", data);
if (typeof appendMessage === 'function') {
appendMessage(data, "received");
}
},
speak: function(message) {
return this.perform('receive', message);
}
});
また、app/assets/javascripts/application.jsに下記を追加する必要があります。
...
//= require action_cable
//= require channels/consumer
//= require channels/index
//= require channels/message_channel
...
このとき依存関係や他assetのエラーなどが原因なのか、上記を挿入する行を変えるとcableでのsocket通信がうまくいきました
これで配信と取得はうまくいくはずです。