IT・技術研修ならCTC教育サービス

サイト内検索 企業情報 サイトマップ

研修コース検索

コラム

Ruby & Rails

CTC 教育サービス

 [IT研修]注目キーワード   Python  UiPath(RPA)  最新技術動向  OpenStack  システムトラブルシュート 

第14回 WebSocketでサーバプッシュ その3 ~Websocket-Rails~ (松永紘) 2014年5月

 4/8にRails4.1.0がリリースされました(*1)。昨年からbeta版やRelease Candidate版としてリリースされていましたので、使ってみた方もいるかと思います。「ActiveRecord enums」や「ActionPack Variants」など新しい機能が盛り込まれていますので、使われたことのない方は是非試してみてください(*2)。

 さて前回はRubyで実装されたWebSocketライブラリ、EM-WebSocketをご紹介しました。このEM-WebSocketを使うと簡単にWebSocketサーバを実装することができますが、Railsアプリケーションと組み合わせる場合は少し手間がかかります。
 そこで今回はRails上で使用できるWebSocketライブラリ、Websocket-Railsをご紹介いたします。

 動作環境は以下の通りです。

  • Mac OS X 10.9.2
  • Chrome 34
  • Ruby 2.1.1
  • Rails 4.1.0
  • Websocket-Rails : 0.7.0

 なお、上記バージョンのWebsocket-RailsはWindows上で動作しないようですのでご注意ください(*3)。

Websocket-Rails

 前述の通り、Websocket-RailsはRails上で動作するWebSocketライブラリです。このライブラリはイベントと呼ばれる単位で、サーバとクライアント間の送受信を行います。

fig01

 サーバサイドではEvent Routerという仕組みで、イベントを対象のアクションにマッピングします。また、クライアントサイドではWebsocket-Rails専用のAPIを使ってイベントのやりとりを行います。

 それでは早速インストールしてみましょう。Gemfileに以下を追記し、「bundle install」を実行します。

gem install "websocket-rails"

 インストール完了後は、以下の手順でWebSocketを使う準備を行います。

  1. 必要なファイルのジェネレート
  2. WebsocketRailsコントローラの作成
  3. Event Routerの設定
  4. クライアントサイドの実装
  5. Rack::Lockの無効化

 ここでは前回EM-WebSocketで作成した、チャット機能を作りながら説明を進めていきます。

fig02

1. 必要なファイルのジェネレート

 Websocket-Railsに必要なファイルの生成などを行うため、「rails g websocket_rails:install」を実行します。実行すると2つのファイルの生成と1つのファイルの編集が行われます。

$ rails g websocket_rails:install
   create config/events.rb
   create config/initializers/websocket_rails.rb
   create app/assets/javascripts/application.js

 events.rbはEvent Routerの定義ファイルで、後述する3.の手順時に編集を行います。websocket_rails.rbはWebsocket-Railsの設定ファイルです(*4)。また、application.jsには以下の行が追加されています。

//= require websocket_rails/main

2. WebsocketRailsコントローラの作成

 続いてWebSocket通信時に処理を行うWebsocketRailsコントローラを作成します。専用のジェネレータは用意されていないようですので、手動でファイルを作成しましょう。ここではapp/controllers/websocket_chat_controller.rbを作成しました。

# WebsocketRails::BaseControllerを継承
class WebsocketChatController < WebsocketRails::BaseController
def message_recieve
  # クライアントからのメッセージを取得
  recieve_message = message()

  # websocket_chatイベントで接続しているクライアントにブロードキャスト
  broadcast_message(:websocket_chat, recieve_message)
end
end

 通常のコントローラと同様にWebsocketRailsコントローラも1つのメソッド(=アクション)を処理の単位として定義します。
WebsocketRailsコントローラ内で使用できるメソッドには、以下のようなものがあります。

メソッド
send_message(event_name, message, options={}) 引数event_nameのイベントとして、クライアントへ引数dataを送信する。
[event_name]
送信するイベント名を指定
[data]
送信するデータを指定
[options]
namespaceなどのオプションがある場合に指定
broadcast_message(event_name, message, options={}) 引数event_nameのイベントとして、全クライアントへ引数dataを送信する。
[event_name]
送信するイベント名を指定
[data]
送信するデータを指定
[options]
namespaceなどのオプションがある場合に指定
message()
data()
クライアントからのメッセージを取得する。

3. Event Routerの設定

 routes.rbではURL/HTTPメソッドとアクションをマッピングしましたが、Websocket-Railsではイベント名とアクションとをevents.rb上でマッピングします。 ここではイベント名を「websocket_chat」として、2.で作成したアクションにマッピングします。

WebsocketRails::EventMap.describe do
 (略)
 # websocket_chatイベントのマッピング
 subscribe :websocket_chat, to: WebsocketChatController,
with_method: :message_recieve
end

 subscribeの第一引数がイベント名、第二引数にコントローラやアクションを指定します(*5)。routes.rbと書き方がほとんどと同じですので、それほど抵抗なく書けるかと思います。

4. クライアントサイドの実装

 次はクライアントサイドの実装です。画面表示用のアクションを定義していないので、ジェネレータでコントローラとビューを作成しましょう。ここではchat#indexとして作成しました。

$ rails g controller chat index
   create app/controllers/chat_controller.rb
   route get 'chat/index'
   invoke erb
   create app/views/chat
   create app/views/chat/index.html.erb
   (略)

 前述の通り、Websocket-Railsでは通常のWebSocket APIではなく専用のAPIを用いてクライアントのJavaScriptを記述します。以下にそのメソッドなどの一覧を示します。

コンストラクタ
new WebSocketRails(url, use_websockets) WebSocketRailsオブジェクトを生成し、WebSocket接続を行う。
[url]
接続先のURLを指定
スキーム(ws://またはwss://)の省略が可能
[use_websockets]
通信にWebSocketコネクションを使用する場合はtrue、HTTPコネクションを使用する場合はfalseを設定
(省略時はtrue)
メソッド
trigger(event_name, data, success_callback, failure_callback) 引数event_nameのイベントとして、引数dataを送信する。
[event_name]
送信するイベント名を指定
[data]
送信するデータを指定
[success_callback]
サーバサイドにてtrigger_successメソッドが呼ばれた際に実行するコールバックを指定
[failure_callback]
サーバサイドにてtrigger_failureメソッドが呼ばれた際に実行するコールバックを指定
bind(event_name, callback) 引数event_nameのイベントを購読する。
[event_name]
購読するイベント名を指定
[callback]
当該イベントがサーバプッシュされた際に実行するコールバックを指定
unbind(event_name) 引数event_nameのイベント購読を破棄する。
[event_name]
購読を破棄するイベント名を指定
disconnect() WebSocket接続を終了する。

 Websocket-Railsを用いたときのクライアントサイドの実装は以下の通りです。

<!-- チャット表示部分 -->
<ul id="chat_area">
</ul>

<!-- コメントフォーム -->
<input id="comment" type="text">
<input id="send" type="button" value="send">

<script>
var ws_rails = new WebSocketRails("localhost:3000/websocket");

// メッセージ受信時の処理
ws_rails.bind("websocket_chat", function(message){
var message_li = document.createElement("li");
message_li.textContent = message;

document.getElementById("chat_area").appendChild(message_li);
})

// メッセージ送信時の処理
document.getElementById("send").onclick = function(){
  var comment = document.getElementById("comment").value;

  ws_rails.trigger("websocket_chat", comment);
  }
</script>

 補足になりますが、WebSocketでの接続先は「ws://ドメイン名/websocket」の形になります。どのような処理でもURLは変わらず、イベント名によってマッピング先を変更します。

5. Rack::Lockの無効化

 developmentモードでWebsocket-Railsを使用する際はRack::Lockを無効にする必要があります。config/environments/development.rbを以下のように編集します。

WebsocketRails::Application.configure do
 (略)
 # 以下を追加
 config.middleware.delete Rack::Lock
end

 完成したら「rails s」でサーバを起動し、「http://localhost:3000/chat/index」にアクセスしてみましょう。EM-WebSocketの時と同様の動きをすることが確認できるかと思います。

fig03

補足:Channelの利用

 EM-WebScoketにおけるEM::Channel(*6)のような仕組みが、Websocket-Railsにも「Channel」として提供されています。ここでは変更点に絞ってご紹介いたします(*7)。

 <サーバーサイド>

class WebsocketChatController < WebsocketRails::BaseController
 def message_recieve
  # この一行を変更
  # broadcast_message(:websocket_chat, message)
  WebSocketRails[:websocket_with_channel].trigger(:websocket_chat, message)
 end
end

 <クライアントサイド>

var ws_rails = new WebSocketRails("localhost:3000/websocket");

// この一行を追加
var websocket_chat_channel = ws_rails.subscribe("websocket_chat_channel");

// この一行を変更
// ws_rails.bind("websocket_chat", function(message){
websocket_chat_channel.bind("websocket_chat", function(message){

 var message_li = document.createElement("li");
 message_li.textContent = message;

 document.getElementById("chat_area").appendChild(message_li);
})

document.getElementById("send").onclick = function(){
 var comment = document.getElementById("comment").value;

 // この一行を変更
 // ws_rails.trigger("websocket_chat", comment);
 websocket_chat_channel.trigger("websocket_chat", comment);
}
まとめ

 前回のEM-WebSocketに引き続き、Websocket-Railsをご紹介してきました。イベント単位での制御やクライアントでの専用のAPIなどはちょっと癖もありますが、RailsとWebSocketとの組み合わせを手軽に行うにはもってこいのライブラリだと思います。 Githubページにはより詳細に説明されているWikiもありますので、是非ご覧ください。

 それでは、Enjoy Ruby!

注釈

*1http://weblog.rubyonrails.org/2014/4/8/Rails-4-1/

*2:詳しくはリリースノート(http://edgeguides.rubyonrails.org/4_1_release_notes.html)をご覧ください。

*3https://github.com/websocket-rails/websocket-rails/issues/186

*4https://github.com/websocket-rails/websocket-rails/wiki/Configuration

*5:第二引数は「controller_name#action_name」と書くこともできます(この例だと「websocket_chat#message_recieve」)。

*6前回のコラムをご覧ください。

*7https://github.com/websocket-rails/websocket-rails/wiki/Working-with-Channels

 


 

 [IT研修]注目キーワード   Python  UiPath(RPA)  最新技術動向  OpenStack  システムトラブルシュート