Rails5になってAction Cableというものが追加されたので、Websocketが使いやすくなったらしいです。実際にやって見ます

Let's try!

STEP1 プロジェクトのセットアップ

rails newやってbundleまでやってください。省略。

STEP2 コントローラの作成とルーティング

コントローラを適当に作ります。

ターミナル
$ rails g controller home index
config/routes.rb
Rails.application.routes.draw do
  root 'home#index'
end

STEP3 Action CableのChannel作成

Action CableのChannelを作成します。これは、いわゆるコントローラだと思ってもらえれば構いません。

ターミナル
$ rails g channel chat

出来上がったChannelを見て見ましょう。

app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
  def subscribed
    # stream_from "some_channel"
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end
end

名前の通りですが、subscribedがクライアントと接続されたとき、unsubscribedは接続が解除されたときに実行されるメソッドです。
subscribedにはクライアントが受信すべき、ストリームを設定します。
ストリームはルーティングに近いもので、そのストリームに対してデータを流すとそのストリームを受信するようにと設定されたクライアントにデータが届きます。

もう1つWebsocketでクライアント的役割を果たす、app/assets/javascripts/channels/chat.coffeeが出来上がっています。

app/assets/javascripts/channels/chat.coffee
App.chat = App.cable.subscriptions.create "ChatChannel",
  connected: ->
    # Called when the subscription is ready for use on the server

  disconnected: ->
    # Called when the subscription has been terminated by the server

  received: (data) ->
    # Called when there's incoming data on the websocket for this channel

名前の通り、connectedが接続の繋がった時、disconnectedが接続の解除が起こった時、receivedが受信をした時に実行されるコールバック関数です。

STEP4 とりあえず繋いでみる

Channelの作成は済んだので、ここからはWebsocketを繋いでみましょう。
まずはChannel側から

app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
  def subscribed
    stream_from 'chat:message' #ここが変わった
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end

  def put_message
    ChatChannel.broadcast_to('message', 'hello')
  end
end

stream_fromによってクライアントが聞くべき、ストリームの名前を指定します。
ChatChannel.broadcast_to('message', 'hello')によって、chat:messageストリームに'hello'を送信しています。ストリームの名前に注意しましょう。
あとChatChannel.broadcast_to('message', 'hello')は普通のコントローラからでも呼び出せます。

app/assets/javascripts/channels/chat.coffee
App.chat = App.cable.subscriptions.create "ChatChannel",
  connected: ->
    # Called when the subscription is ready for use on the server

  disconnected: ->
    # Called when the subscription has been terminated by the server

  received: (data) ->
    console.log(data)

  put_message: () ->
    @perform('put_message')
    return

@perform('put_message')によって、ChatChannelput_messageメソッドを呼び出すことができます。

とりあえず実行してみましょう。
スクリーンショット 2017-01-29 0.03.39.png
インスペクタを使って、put_message()を呼び出します。うまく行くとこんな感じになります。

STEP5 チャットにしよう

もうだいたいやることはわかったので仕上げに取り掛かります。

app/views/home/index.html.erb
<%= form_tag '#', id: 'message' do %>
  <%= text_field_tag 'body' %>
<% end %>
<ul id="message-list">
</ul>
app/assets/javascripts/application.js
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .

window.addEventListener('load', () => {
   document.getElementById('message').onsubmit = () => {
       App.chat.put_message(document.getElementById('body').value);
       return false;
   }
});
app/assets/javascripts/channels/chat.coffee
App.chat = App.cable.subscriptions.create "ChatChannel",
  connected: ->
    # Called when the subscription is ready for use on the server

  disconnected: ->
    # Called when the subscription has been terminated by the server

  received: (data) ->
    li = document.createElement('li')
    li.textContent = data
    document.getElementById('message-list').appendChild(li)

  put_message: (msg) ->
    @perform('put_message', { message: msg })
app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
  def subscribed
    stream_from 'chat:message' 
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end

  def put_message(data) #ここが変わった
    ChatChannel.broadcast_to('message', data['message']) #ここが変わった
  end
end

特にコメントすることはないのですが、@perform('put_message', { message: msg })の第2引数(サーバに送られるデータ)は、ハッシュ/配列じゃないとダメそうです

タブを2つくらい開いてメッセージを送って、一方のタブで入力/送信した内容が他方にも届いていたら完成です!