RailsのActionCableでチャット機能を作ってみた


#初めまして
初めまして。初投稿でございますが、よろしくお願いします。早速ですが、今回はrails5の
機能であるaction cableを使ってチャット機能を作る機会があったので、アウトプットしてみようと思います。
なおプログラミングという分野を初めて５ヶ月の私に少しでもフィードバックが頂けると
幸いです。
初めての投稿で使い方がわからないand慣れない僕の記事。暖かく見守ってやってくださいw
今回、一度コーディングをしてその後に一つずつ順番に解説するスタイルを取っています。一度、
皆さんにも、動くものを作ってもらった後に、なぜこのように動くのかという部分をみてもらいたいと思います。

https://qiita.com/port-development/items/120b2bee0cef505a4005

今回上記のURLを参考にしつつ、DBへ保存する処理は自分で実装しました。僕が、Qiitaでの

初めて故にわかりづらい場合はこちらの記事を参考にして頂いた上でDBの保存する処理だけでも見ていた

だければと思います。

#action cableとは

railsアプリケーション上でwebsocket通信による、双方向通信を簡単に実現するための

新しい機能。ちなみにwebsocketとは、サーバーを介してwebブラウザ間で、双方向通信を行う機能。

以前は、rails上でwebsocketを実装するには、複雑な処理を書く必要が

あったそうですが、自分はrails5世代ですのでそんな歴史は知らず・・w

ではどんな機能があるのかを書いていきます。


#主な機能

・コネクション
websocket通信をサーバーとクライアント側で接続する機能。繋げるだけ。

・チャンネル(channel)
websocket通信で接続するサーバー側の通信。つまりサーバー側の実装。

・クライアント
websocket通信を使う上で必要なクライアント側の仕組み。ボタンを押すとサーバー側に情報をPOST送信するなど。

・ジェネレイター
rails g コマンドを使った上記のチャネル、クライアント側のファイルの自動生成。

#実装スタート

```shell
mkdir chat(ディレクトリ名)　(ディレクトリの作成)

cd chat(ディレクトリ名)　（ディレクトリへ移動）

rails new railsアプリケーション名 （railsアプリケーションの作成）
```

個人的にcoffeescriptがあまり好きではないので、こちらの機能を削除するのと、Jqueryを
GemFileに追加します。

```rb
gem 'jquery-rails'
gem 'coffee-rails' ~> # 削除
```

追加と削除を行ったら

```shell
bundle install
```

一度反映させましょう。
では、ここでチャットのviewとcontrollerを書いていく。

```shell
rails g controller top
```

次にconfig/routes.rbにルーティングを設定します。

```rb
get "/" => "top#show"
```

ルーティング後にモデルを作成していきます。

```shell
rails g model Text text:text
```

テキストテーブルにテキストカラムでデータ型をテキストで設定しています。（わかりづらくてすいません）

views/show.html.erb

```erb
<h1>Chat</h1>
<ul id="chat-index">
  <% @text.each do |message| %>
  <li><%= message.text %></li>
  <% end %>
</ul>
<%= form_tag("/create") do %>
  <input id="chat-form" type="text" data-behavior="chat_post" name="message" value="入力">
<% end %>
```

このようにコーディングしてきます。ここでは、後に、javascriptのイベント処理で指定するためのidを指定しているんだなと思ってれば大丈夫だと思います。form_tagあたりについては、下記でcontrollerの処理について述べていますが結局何をしているのかと言いますと、入力フォームで送信したデータをDBに保存する処理をform_tagで実現しました。

その後、app/controllers/top_controller.rbに

```rb
class TopController < ApplicationController
  def show
    @text = text.all
  end

  def create
    @texts = Text.new(text.params[:message])
    @texts.save
  end
end
```

ここでは、テキストテーブルのデータを全て受け取るshowアクションを定義して、その後、createアクションで、show.html.erbファイルのinputタグから送信されたデータをparamsで受け取りデータベースに保存する処理を書いています。

ここまでは普段書いているrailsなのですが、ここから変わってきます。

```shell
rails g channel chat post 
```

このコマンドを実行してchannelを作ります。一番初めに書いた通り、channelはサーバー側の実装になります。
上記のコマンドを実行すると下記のようなファイルができます。

app/channels/chat_channel.rb

```rb
class ChatChannel < ApplicationCable::Channel
  def subscribed
    stream_form 'chat_channel'
  end
  
  def unsubscribed
  end

  def post(data)
    ActionCable.server.broadcast('chat_channel', data)
  end
end
```

chat_channels.rbでは今回は機能がシンプルなため遠回りしている印象を受けますが、実際はここでDBと接続したり外部アプリと連携したりできます。

app/assets/javascripts/channels/chat.js

```js
App.chat = App.cable.subscriptions.create("ChatChannel", {
  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){
    return $('#chat-index).append('<li>' + data.message + '</li>');
  }, 
  
  $(document).on('keypress', '[data-behavior~=chat_post]', function(event){
    if(event.keyCode === 13){
      var chatForm = $('#chat-form');
      App.chat.post(chatForm.val());
      return;
    }
  })
);
```

そしてこちらのファイルでは、第一引数に今回設定したチャンネル名、そして第二引数には、サーバー側での処理が記載されています。receivedに注目してください。こちらのreceivedではクライアント側から受け取ったデータ(今回の場合、入力フォームから受け取ったデータ)をどの要素に
返すかを指定しています。ここで、先ほど記載したshow.html.erbファイルを見てもらいたいのですが

```erb
<ul id = "chat-index"></ul>
```

この部分にサーバー側で受け取ったデータをappendすることを指定しています。その後、第三引数に下記のイベントの処理を指定しています。

```js
$(document).on('keypress', '[data-behavior~=chat_post]', function(event){
    if(event.keyCode === 13){
      var chatForm = $('#chat-form');
      App.chat.post(chatForm.val());
      return;
    }
})
```

data-behaviorという部分で指定したchat_postはデータを送信する前のクライアント側での処理をどの
要素で行うかを指定しています。そしてその次のfunctionでどういった処理をするのかを指定しています。

ここまででこのようなchatができるかと思われます。


<img width="1315" alt="スクリーンショット 2018-05-16 0.02.10.png" src="https://qiita-image-store.s3.amazonaws.com/0/238185/e704db69-8e86-b863-3807-1d5cc0872bee.png">

この入力フォームに文字列を入れると。。
<img width="958" alt="スクリーンショット 2018-05-16 0.23.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/238185/dfe4d619-eb0f-1686-f309-e257a6802946.png">


このように表示されます。

今回は、チャットの機能実装のためのアウトプットという形で投稿しました。
もし見てくださった方でもっとこうした方がいいなど（記事の書き方、コーディング、デバック）アドバイスがありましたら是非コメントくださると嬉しいです。