ActionController::Live を試す

Rails4から追加されたこの機能を使うとstreamingっぽいことやpush配信ぽいことが可能です。
 
Upgrading to Rails 4 の中で ActionController::Live のsampleコードが記載されていたので試してみました(P.62あたり)。
 

準備

thread-safeなwebサーバであるpumaなどを利用する必要があります。

# Gemfile
gem 'puma'

$ bundle

 

写経

Rails

Rails側は、include ActionController::Live を追加して
pushで配信したい内容を書くだけです。

class TimerController < ApplicationController
  include ActionController::Live

  def tick
    response.headers['Content-Type'] = 'text/event-stream'

    begin
      seconds = 0
      loop do
        response.stream.write("data: #{seconds}\n\n")
        seconds += 1
        sleep 1
      end
    rescue IOError
      # client dsconnected
    ensure
      response.stream.close # cleanup
    end
  end
end

ここでは、loopで回しているだけですが、tenderloveさんのsample のようにFSEventを使ってファイルの変更を検知して表示なんてことも可能です。
 

developmentではthread safeとして起動しないため下記を変更する必要があります。

# config/environments/development.rb
  # 追加
  config.middleware.delete ::Rack::Lock
  # 変更
  config.eager_load = true

 
Routing

Rails.application.routes.draw do
  get '/tick' => 'timer#tick'

フロント

html5のServer-Sent Eventsの実装であるEventSourceを利用します。

<!DOCTYPE html>
<html>
<head>
  <title>Timer Example</title>
  <script src="http://code.jquery.com/jquery.js"></script>
  <script>
    $(function() {
      var timerSource = new EventSource('/tick');
      timerSource.onmessage = function(e) {
        $('#timer').text(e.data);
      };
    });
  </script>
</head>
<body>
  Timer: <span id="timer"></span> seconds.
</body>
</html>

Server-Sent Eventsの詳しいところまでは理解できていないのですが、なかなか面白そうです。
・WebSocketsと異なりhttpでやりとりする(素晴らしい!!)
・双方向通信ではない
IEが未サポート

 
上記の書き方はeventっぽくないのですが
下記のようにeventを定義してやることも可能です。
 
rails

response.stream.write "event: update\n"

フロント

timerSource.addEventListener('update', function(e) {
  $('#timer').text(e.data);
});

eventがなければonmessageが動作するようです。
 

関連

Upgrading to Rails 4
これとてもよいです。