更新情報

こんにちは、トランスネットの友村です。

先日、当サイトの「Ruby on Rails海外事情コラム」にて「RailsConfでDHHが語ったRails 5をひとつまみ」を公開しました。
今回はRails5で追加予定の目玉機能のうちの一つである「ActionCable」について、
少し早いですがRailsリポジトリのサンプルプログラムを利用して実践してみたいと思います。

ActionCable

「ActionCable」を利用することによって、「WebSocket」通信を利用したサーバ・クライアント間の双方向通信を行うことが出来るようになります。
「WebSocket」とはHTML5から追加された通信規格でサーバ・クライアント間でコネクションを確立することによりデータのやりとりをリアルタイムで行うものです。
チャットなどのリアルタイム制が求められるツールを想像してもらうと分かりやすいかと思います。
「ActionCable」はクライアント側(JS)とサーバ側(Ruby)へ機能を提供します。
では実際にやってみたいと思います。

 

サンプルプログラムを動かす

Railsのリポジトリでサンプルプログラムが公開されていますのでこちらを利用します。(https://github.com/rails/actioncable-examples)

 

  • redisの導入

    まず、「ActionCable」を利用するにあたり「redis」の導入が必要になります。
    「redis」はデフォルトで6379番のポートを利用しますので、ファイアーウォールなどが機能している場合は適宜開放して下さい。

    インストール方法はREADMEに書かれている部分から抜粋して紹介しますが環境に合わせて行っても問題ありません。

    On Linux

$ wget http://download.redis.io/redis-stable.tar.gz
$ tar xvzf redis-stable.tar.gz
$ cd redis-stable
$ make
$ make install

On Mac
$ brew install redis
  • ./bin/setupを実行・サーバ起動
次に./bin/setupを実行します。と書いてありますが、何が起こるがわかり無いので中身を確認します。
せっかくなので一行ずつコマンドで実行してしまいます。
$ bundle install –path=vendor/bundle
$ rake db:setup
$ rake db:seed
$ ./bin/cable
$ redis-server
$ rails s
=> Booting Puma
=> Rails 5.0.0.alpha application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
Puma 2.11.3 starting…
* Min threads: 0, max threads: 16
* Environment: development
* Listening on tcp://0.0.0.0:3000

動作確認

ここまで問題なく来ればあとは動作を確認してみましょう。
http://localhost:3000

ずいぶんと素っ気ない画面が出来てました。使い方がまだ分からないので、指示に従いユーザを選びます。
screencapture (1)
どうやらログインしたようです。リンクが一つしか無いので下記の選択肢を選択します。
チャットアプリですね。
screencapture (2)

次にチャットルームを選択します。
screencapture (3)

コメント投稿エリアが出現してきました。後は、ブラウザを2つ起動して確認したり、同僚の方とチャットをしてみたりと楽しんで下さい。
screencapture (4)
動作確認は出来ましたでしょうか?
私の環境では「Vagrant」を使用していたため、「WebSocket」の通信用ポートをポートフォワードしてやる必要がありました。
うまく動作しない場合はクライアント側のエラーにも注目してみてください。ブラウザの開発ツールで確認出来ます。
プログラムを確認する

ここまでくれば気になるのはどういうプログラムで実装されているかです。

まずはコメントのコンテンツが保存されるCommentモデルを確認します。
5行目に注目してみてください。コミット後に自身を引数にJobの呼び出しを行っています。

  • actioncable-examples/app/models/comment.rb
class Comment < ActiveRecord::Base
  belongs_to :message
  belongs_to :user
  after_commit { CommentRelayJob.perform_later(self) }
end
view rawgistfile1.rb hosted with ❤ by GitHub

この流れに従いCommentRelayJobクラスを確認しましょう。
ここでActionCableクラスの登場です。処理をみてみるとすべてのコネクションにブロードキャストしているようです。
これをクライアントで受信した際にDOMを更新しているのでしょう。クライアント側を確認しましょう。

  • actioncable-examples/app/jobs/comment_relay_job.rb
class CommentRelayJob < ApplicationJob
  def perform(comment)
  ActionCable.server.broadcast “messages:#{comment.message_id}:comments,
  comment:CommentsController.render(partial:comments/comment, locals: { comment: comment })
  end
end
view rawgistfile1.rb hosted with ❤ by GitHub

発見しました。
11行目received関数の引数で送られてきたメッセージを受け取り、DOMにappendしていますね。
とても簡単です。

  • actioncable-examples/app/assets/javascripts/channels/comments.coffee
App.comments = App.cable.subscriptions.create CommentsChannel,
collection: -> $([data-channel=’comments’])
connected: ->
# FIXME: While we wait for cable subscriptions to always be finalized before sending messages
setTimeout =>
@followCurrentMessage()
@installPageChangeCallback()
,1000
received: (data) ->
@collection().append(data.comment) unless @userIsCurrentUser(data.comment)
userIsCurrentUser: (comment) ->
$(comment).attr(data-user-id) is $(meta[name=current-user]).attr(id)
followCurrentMessage: ->
if messageId = @collection().data(message-id)
@perform follow, message_id: messageId
else
@perform unfollow
installPageChangeCallback: ->
unless @installedPageChangeCallback
@installedPageChangeCallback = true
$(document).on page:change, -> App.comments.followCurrentMessage()
view rawgistfile1.coffee hosted with ❤ by GitHub

まとめ

いかがでしたでしょうか?
DHHが以前から言っている「1人、あるいは少人数のチームのためのRails」という考えが、
バージョンアップされる度にどんどん実現されているように感じます。
仮に1人でスタートアップを行うとしても不安を感じることは少ないでしょう。
Rails 5の今後の動向に注目です。

* この記事は執筆時点(2015/7/17)の情報であり今後変更される可能性あります。