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

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

#完成コード
tableはmessage,column名はbodyでdbを作成

```ruby: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
```

```javascript: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など少し組み込んだ

```ruby: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>

```

```javascript: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になる。
<details><summary>keycodeとは？</summary>
http://shanabrian.com/web/javascript/keycode.php
キーボードを押すと、キーコードが発行される。enterは13番。
用いればどのキーボードを押したかがわかる。
</details>
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://gyazo.com/99e7e354fe6fc0e80d9f4392c8bd880b/raw)

##参照した記事
https://railsguides.jp/action_cable_overview.html
https://codezine.jp/article/detail/10153?p=1
https://qiita.com/fujisawaryohei/items/3a567a64691276da0d45