LoginSignup
3
5

More than 3 years have passed since last update.

rails WebSocket と ActionCableを用いたリアルタイム通信

Last updated at Posted at 2019-10-24

なぜやろうと思ったか

railsで非同期通信を行った時に、自分のwebブラウザしか結果が反映されない問題を解消したかった。
前まで、setIntervalで10秒ごとに読み込みさせて認識させていたんですが、変化がない時にも動くので何か他の方法がないかなぁ〜と模索していました。

完成コード

tableはmessage,column名はbodyでdbを作成

channels/chat_message_channel.rb
class ChatMessageChannel < ApplicationCable::Channel
  def subscribed;stream_from "chat_message_channel";end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end

  def speak(data)
    @message = Message.create(body: data["message"])
    data['id'] = @message.id
    ActionCable.server.broadcast 'chat_message_channel',message: data
  end
end
message.js

App.chatmessage = App.cable.subscriptions.create("ChatMessageChannel", {
  connected: function() {
    // Called when the subscription is ready for use on the server
  },

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

  received: function(data) {
    let message_id = data['message']['id']
    let message_text = data['message']['message']
    let html =`
      <tr>
      <td><a href="/messages/${message_id}">Show</td>
      <td><a href="/messages/${message_id}/edit">Edit</td>
      <td><a data-confirm="Are you sure?" rel="nofollow" data-method="delete" href="/messages/${message_id}">Destroy</td>
      <td>${message_text}</td>
      </tr>`
        $("#message").append(html)
    // Called when there's incoming data on the websocket for this channel
  },

  speak: function(message) {
    return this.perform('speak',{message: message});
  }
  }, $(document).on('keypress', '[data-behavior~=speak_chat_messages]', function(event) {
  if (event.keyCode === 13){
   App.chatmessage.speak(event.target.value)
    event.target.value = ''
    event.preventDefault()

  }
}));

rails g scaffoldで作成したindex.html.erbにidなど少し組み込んだ

index.html.erb
<p id="notice"><%= notice %></p>

<h1>Messages</h1>

<table>
  <thead>
    <tr>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody id="message">
    <% @messages.each do |message| %>
      <tr>
        <td><%= link_to 'Show', message %></td>
        <td><%= link_to 'Edit', edit_message_path(message) %></td>
        <td><%= link_to 'Destroy', message, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      <td><%= message.body %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>
<%= link_to 'New Message', new_message_path%>

</div>
<br/>
<div class="aaa"><%= @message.body %></div>
<form>
  <label>
    websocketsで送信: <input type="text" data-behavior="speak_chat_messages">
  </label>
</form>

javascripts/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);

主にこの4つのfileをいじってもらいことになりますね。

cable.js

まず最初に読まれるfileです。
クライアントからサーバーに対してWebSocket接続しているコードで、
App.cable = ActionCable.createConsumer();でWebSocket通信を確立している。
他のサイトにはcoffee fileを呼ぶものがありますが、今回はmessage.jsに記述を書いていきます。

message.js

App.chatmessage = App.cable.subscriptions.create("ChatMessageChannel", {
でcable.jsからmessage.jsに接続している。
成功すれば、connectedに飛び、失敗したもしくは拒否した場合はdisconnectedに飛ぶ。

その後、inputのkeypress functionを動かす。
enterキーを押すと,if (event.keyCode === 13)の条件がtrueになる。

keycodeとは?
http://shanabrian.com/web/javascript/keycode.php
キーボードを押すと、キーコードが発行される。enterは13番。
用いればどのキーボードを押したかがわかる。

preventDefaultでrailsのhttp通信を中断し、その代わりにjqueryのApp.chatmessage.speak functionを実行する。引数にevent.target.value(
keypressしたinputの中身)を定義。

次に、speak: function(message) が呼ばれます。
return this.performでサーバーのChat_message_Channel#speakを呼び出す。
仮に、perform('appear',message: message)なら Chat_message_Channel#appearを呼び出す。
第二引数には渡したい値をハッシュで渡してあげます。
{performはrailsのメソッドだと思われ、サブスクリプションでperformメソッドを使って、RPC(リモートプロシージャコール)として利用できるみたい}
よくわかりません。。。。(・・;)
https://railsguides.jp/action_cable_overview.html

Chat_message_channel.rb

speak actionが呼ばれました。ここでは、railsのcontrollerの役割を担う部分になります。
情報をdbにsaveしたりできます。
speak(data)におけるdataの中身はハッシュで受け取ってます。params的な感じで。
data=> {"message"=>"111111", "action"=>"speak"}
今回は、saveした@message.idをviewで使用するので、dataのハッシュにidを入れ込みます。
この時点でのdataはこんな感じ。
data=> {"message"=>"111111", "action"=>"speak", "id"=>125}


 speakメソッドのActionCable.server.broadcastでは第1引数にチャネル名、第2引数に発言メッセージを指定することで、サーバー側からChatMessageChannelにWebSocketで接続している全クライアントに対して発言メッセージを配信することが可能になります。
これで、全クライアントに対して処理を行う準備ができました。
メッセージを配信したので、受け取りを行わなければなりませんね。

message.js

message.jsに戻ってきました。
配信されたdataをここのreceived functionで受け取ります。
message: {message: "1111", action: "speak", id: 128}
__proto__: Object

dataはこんな感じなので取り出し方は、data['message']['id']みたいな感じ。
その後、作成したhtmlを追加する処理を書いて終了となります。。

最後に

参考にした記事にはcoffee fileを用いて使う手法もありましたが、利便性が悪いのでjqueryを使用してやってみました。
これで、リアルタイムのchatが作れるよ〜〜〜。
完成動作

参照した記事

https://railsguides.jp/action_cable_overview.html
https://codezine.jp/article/detail/10153?p=1
https://qiita.com/fujisawaryohei/items/3a567a64691276da0d45

3
5
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
3
5