
By: Samantha Brough – CC BY 2.0
ActionCableとは
ActionCableは、railsでWebSocketを利用したリアルタイム通信を可能にするgem.
rails5から、railsの本体に取り込まれる.
github.com/rails/actioncable
詳しい情報は、
github.com/rails/actioncable/tree/archive
公式サンプルは、ここ
github.com/rails/actioncable-examples
公式サンプルの動画解説は、ここ
gorails.com/episodes/rails-5-actioncable-websockets
今回は、この記事のチャットアプリを試してみた.
Getting started with Rails 5’s ActionCable and websockets
rails4での利用
Gemfileに追加する.
サーバーはpumaを使うので、これも追加しておく.
今回は、ruby 2.2.1 rails 4.2.5の環境で実行した.
1 2 | gem 'actioncable', github: 'rails/actioncable' gem 'puma' |
redisサーバーの準備
macの場合、brewでインストールしておく.
1 | brew install redis |
redisの仮起動は次の通り.
1 | redis-server /usr/local/etc/redis.conf |
cableサーバーの準備
ApplicationCable::ConnectionとApplicationCable::Channel
まず、2つのクラスを作る必要がある.
app/channels/application_cable/以下にファイルを2つ作る.
1 2 3 4 5 | # app/channels/application_cable/connection.rb module ApplicationCable class Connection < ActionCable::Connection::Base end end |
ApplicationCable::Connection では、入ってくる接続を許可したりする.
1 2 3 4 5 | # app/channels/application_cable/channel.rb module ApplicationCable class Channel < ActionCable::Channel::Base end end |
ApplicationCable::Channel では、Channel間で共通のロジックを書いたりする.
config/redis/cable.yml
redisサーバーのpubsubを使っているらしいのでredisの設定ファイルも作る.
:が前後にあるので注意.
1 2 3 4 5 6 7 8 | local: &local :url: redis://localhost:6379 :host: localhost :port: 6379 :timeout: 1 :inline: true development: *local test: *local |
cable/config.ru
cableサーバーは別プロセスで動くので、それ用のrackファイルを作成する.
1 2 3 4 5 6 | require ::File.expand_path('../../config/environment', __FILE__) Rails.application.eager_load! require 'action_cable/process/logging' run ActionCable.server |
bin/cable
サーバー起動用のスクリプトファイルを作っておく.
1 2 | # /bin/bash bundle exec puma -p 28080 cable/config.ru |
次の通り実行権限を与えておく.
1 | chmod +x bin/cable |
cableサーバーの起動
これで、 bin/cable でcableサーバーが起動する.
チャットアプリの内容
cable部分以外のRailsのプログラムの流れ
1 root ‘sessions#new’ → views/sessions/new.html.erb
config/routes.rbでルートアドレスにアクセスすると、まず、views/sessions/new.html.erbに飛ぶ
views/sessions/new.html.erbではusernameをPOSTするフォームがある.
2 POST ‘sessions#create’
usernameをPOSTすると、sessions_controllerでcookie.signed[:username]に保存.
その後、messages_pathにリダイレクト.
3 views/messages/index.html.erb
views/messages/index.html.erbには、フォームからremoteで[:message][:body]を/messagesにPOSTできる.
また、div id=”messages” があってmessages一覧を表示する場所が用意されている.
Channelクラスの作成
クライアントがChannelクラスを購読(subscribe)すると、subscribedメソッドが呼ばれる.
1 2 3 4 5 6 | # app/channels/messages_channel.rb class MessagesChannel < ApplicationCable::Channel def subscribed stream_from 'messages' end end |
stream_fromメソッドは、名付けられたbroadcastingからstreamを開始する.
www.rubydoc.info/github/rails/actioncable/ActionCable%2FChannel%2FStreams%3Astream_from
上で定義したApplicationCable::Channelを継承して使う.
‘messages’ streamにbroadcastする
1 2 3 4 5 6 7 8 9 | class MessagesController < ApplicationController def create ActionCable.server.broadcast 'messages', message: params[:message][:body], username: cookies.signed[:username] head :ok end end |
*今回はデータベースに保存しないので単純にcontrollerからbroadcastしてるけど、DHHのサンプルでは、Modelで保存後にJobを呼び出してBroadcastしている.
クライアントサイドでcableサーバーに接続
cableをrequireして、JaveScript(CoffeeScript)でサーバに接続する.
1 2 3 4 5 6 | #= require cable #= require_self #= require_tree . @App = {} App.cable = Cable.createConsumer 'ws://127.0.0.1:28080' |
また、application.jsで、 require_tree .の前に、 channelsライブラリをrequireしておく.
1 | //= require channels |
クライアントサイドでChannelを購読する.
クライアントがWebSocketから何かを受け取ると、App.messages.received関数が呼び出される.
1 2 3 4 5 6 | App.messages = App.cable.subscriptions.create 'MessagesChannel', received: (data) -> $('#messages').append @renderMessage(data) renderMessage: (data) -> "<p><b>[#{data.username}]:</b> #{data.message}</p>" |
できた
こんな感じでブラウザを2つ並べると、リアルタイムで更新されることがわかる.
Rails5では、アクションの外でrender viewsができるようになるらしい.
これを使えば、Javascriptの代わりにhtmlをそのまま発行できる.
1 2 3 4 5 6 7 8 | ActionCable.server.broadcast 'messages', message: MessagesController.render( partial: 'messages/message', locals: { message: params[:message][:body], username: cookies.signed[:username] } ) |