ワシはワシが育てる

週刊少年ジャンプと任天堂のゲームが三度のメシより好きです。

RailsでWebSocketを使う

タイトルの通り。 HTML5の標準規格から外されたものの、次世代に欠かせない機能の一つになるでしょう。

ただしandroidブラウザーは最新4.4以降から、IEは10以降からとブラウザ制約があるので注意が必要です。

使い方は非常に簡単です。

# gemに下記を追加
gem 'websocket-rails'

# 以下のコマンドでevent.rbを生成
rails g websocket_rails:install

# development.rbに以下を追加
config.middleware.delete Rack::Lock

下準備はこれだけです。
githubを見るとthinサーバーでスタートしてますが、イベント駆動に対応したサーバであれば良いのでデフォルトのWebrickでも問題ありません。

原理としては、クライアントのjavascriptとサーバサイドで先ほど生成したevent.rbでイベントを送信・受信しあって、それぞれ処理を書くだけです。

# event.rbのEventMapに追加
subscribe :new_message, to: ChatController, with_method: :new_message

これで「クライアントからnew_messageを受け取り、それをChatControllerのnew_messageメソッドで処理しますよ」ということをサーバーサイドに指定しています。
そしてクライアントから送信するには以下のようにJSを記述します。

# これはcoffeescriptで書いています

#= require websocket_rails/main
# ↑WebSocketを利用するための初期設定ファイル

class @ChatClass
  constructor: (url, useWebsocket) ->
    # これがソケットをつなぐ重要なもの
    # WebsocketRailsのインスタンスを生成するには上のrequireが必要
    @dispatcher = new WebsocketRails(url, useWebsocket)
    # イベントを監視
    @events()

  events: () =>
    # ボタンが押されたらメッセージを送信
    $('#button').on 'click', @submitMessage
    # サーバーからsend_messageを受け取ったらreceiveMessageを実行
    @dispatcher.bind 'after_send_message', @receiveMessage

  submitMessage: (event) =>
    # サーバ側にsend_messageのイベントを送信
    # オブジェクトでデータを指定
    @dispatcher.trigger 'send_message', { body: 'hogehoge' }

  receiveMessage: (message) =>
    # 受け取ったデータをとりあえずlog
    console.log message

$ ->
  window.chatClass = new ChatClass('http://localhost:3000/websocket', true)

こんな感じで、「@dispatcherがサーバとの架け橋になるよ」「ボタンを押したらsend_messageのイベントを送るよ」「サーバからafter_send_messageが来たらreceiveMessageを実行するよ」と指定します。

それではsend_messageのイベントをサーバー側のChatControllerで処理します。

# ChatController(場所は普通のコントローラーの場所で構いません)
class ChatController < WebsocketRails::BaseController
  def new_message
    # messageという変数が送られてくる
    send_message :after_send_message, message
  end
end

send_messageを受け取ったらafter_send_messageのイベントを送信し、先ほどのjavascriptでreceiveMessageが実行される、という流れになります。
※「send_message :after_send_message」の「send_message」は基本構文です。

このような形でイベントを随時追加することでリアルタイム処理を実行することができます。
ライブラリの機能としてネームスペースやルームにも対応しているので、スケーラビリティも十分です。

開発に際してJSファイルを分割するのが通例かと思いますが、その時には「dispatcher」をグローバル化し、他のファイルで別途イベントリスナーすれば良いでしょう。

ちなみにnode.jsのsocket.ioも仕組みはほぼ同じなので、一方を学んでいれば汎用性があります。
ただsocket.ioはクロスブラウザ対応である一方、node.jsにかなり癖があるので個人的にはRailsを扱える点が美味しいですね。

※続きの記事→http://washiiku.hatenablog.com/entry/2014/03/22/063255