prompt bowl company handful sort yourself tribe operatingexplosion lawyer adult hard writer they resort economist
seen from United States
seen from Singapore
seen from United States
seen from China

seen from China
seen from Singapore
seen from Russia
seen from Australia
seen from T1
seen from China
seen from Singapore
seen from United States
seen from United States

seen from United States

seen from United States

seen from United States

seen from United States

seen from United States
seen from T1
seen from China
prompt bowl company handful sort yourself tribe operatingexplosion lawyer adult hard writer they resort economist
Puzzling over Action Cable. Trying to figure out how to implement a channel subscription that only occurs when a user is viewing a given page (and unsubscribes when the user leaves). I’m mostly confused because Action Cable seems to be set up so that the user subscribes to a channel as soon as they visit the site, by default.
3分クッキングActionCable
FiNCサーバサイドエンジニアの篠塚です。 先日Tokyo Rubyist Meetupというイベントで登壇し、Rails 5の新機能の紹介とActionCableを用いたチャットアプリケーション作成のライブコーディングをしました。
イベント詳細は当社Webエンジニアの大谷がブログにてレポートしてくれています。 本ブログではスライドには記載していないデモ内容を通して、ActionCableの使用方法について説明します。
まずはじめにActionCableについて
ActionCableはRails 5 から導入された新機能で、WebSocketを用いた双方向通信の実装が容易にできるようになります。 本ブログの後半でも紹介しますが、クライアントサイド(JavaScript)とサーバサイド(Ruby)の両フレームワークが提供されています。
WebSocketって?
ActionCableの紹介に入る前に簡単にWebSocketについておさらいします。
WebSocketは双方向通信のための通信プロトコルです。Webで利用されるHTTPは元々リクエストに対してサーバがレスポンスをするというプル型のプロトコルであるため、イベントが双方向に通知されるといったリアルタイムWebの要求を満たすことが困難でした。
その要求に対してHTTP上で解決しようとした試みがポーリングやComet(ロングポーリング)といった技術です。 ポーリングは一定時間おきにリクエストを送ることによりリアルタイム性を実現しようとしています。ポーリングは実装が容易というメリットがありますが、サーバ側の状態が変更されていないときには無駄なリクエストを送ってしまうことになります。 Comet(ロングポーリング)では、リクエストを受け取ったサーバがすぐにレスポンスを返さずに、サーバ側の状態が変更された時にレスポンスを返すことでポーリングに比べてリクエスト回数を減らしています。しかし、サーバ側の更新頻度が上がると接続回数が増えて効率が悪くなってしまいます。
他にもこれらの技術では毎回TCPコネクションを貼り直すことや、毎回のHTTPメッセージのサイズが大きいという問題があります。
WebSocketではクライアントとサーバで一度接続を確立するとその接続を保持するため、サーバ側からも任意のタイミングでメッセージを送信することが出来るようになり、また毎回のメッセージサイズも小さくする事が出来ます。
ActionCableを使ってみる
それではActionCableを利用して双方向通信可能なチャットアプリケーションを作成します。作成は以下の手順で行います。
Railsプロジェクトの作成と設定
Channelの作成
クライアントサイド実装
クライアントとサーバの統合
異なるブラウザからメッセージを送って結果を確認
Rails 5のインストールは済んでいるものとして割愛します(執筆時点では正式リリースがまだなので5.0.0.rc1を利用しています)。 実装内容はDHHのデモ動画を参考にしています。
Railsプロジェクトの作成と設定
まずRailsプロジェクトを作成し、database.ymlの設定を行いdbを作成します。Rails 5からrakeコマンドがすべてrailsコマンドで呼び出せるようになったのでrails db:createとしてdbを作成します。
$ rails new acpj -d mysql $ cd acpj $ # configure database.yml $ rails db:create
次に、コントローラとモデルの作成をします。
$ rails g controller rooms $ rails g model message message:string $ rails db:migrate
ルートを'rooms#show'に変更し、コントローラとビューを実装します。
# config/routes.rb Rails.application.routes.draw do root to: 'rooms#show' end
# app/controllers/rooms_controller.rb def show @messages = Message.all end
# app/views/rooms/show.html.erb <h1>RoomChannel</h1> <div id="messages"> <%= render @messages %> </div> <form> <label>Send Message!!</label><br> <input type="text" data-behavior="message_sender"> </form>
# app/views/messages/_message.html.erb <div class="message"> <%= message.message%> </div>
Channelの作成
Rails 5から追加されたジェネレータを用いてChannelを作成します。これによりroom_channel.rbとroom.coffeeの2ファイルが作成されます。
$ rails g channel room # app/channels/room_channel.rb # app/assets/javascripts/channels/room.coffee
room_channel.rbにはsubscribedとunsubscribedのメソッドが、room.coffeeにはconnected, disconnected, receivedのメソッドが予め用意されています。
クライアントサイドの実装
次にroom.coffeeファイルの実装をします。 フォームからテキストを送信した時にchannelをsubscribeしているconsumerにメッセージが送信されるようにします。 ここでconsumerとはWebSocketにおけるクライアントのことを指し、subscribeとは特定のチャネルとの間にコネクションを維持することを指します。
# app/assets/javascripts/channels/room.coffee # 略 received: (data) -> ('#messages').append data['message'] send_message: (message) -> @perform 'send_message', message: message $(document).on 'keypress', '[data-behavior~=message_sender]', (event) -> if event.keyCode is 13 App.room.send_message event.target.value event.target.value = '' event.preventDefault()
これによりフォームからメッセージが投稿されるとsend_messageメソッドが呼ばれるようになります。
クライアントとサーバの統合
最後にサーバサイドのroom_channel.rbを実装します。 またActionCcontroller.renderメソッドはRails 5から追加された関数で、controller以外からビューをrenderできます。
# app/channels/room_channel.rb # 略 def subscribed stream_from 'room_channel' end def send_message(data) message = Message.create!(content: data['body']) ActionCable.server.broadcast( 'room_channel', message: render_message(message) ) end private def render_message(message) ActionController.render( partial: 'messages/message', locals: { message: message } ) end
異なるブラウザからメッセージを送って結果を確認
あとはrailsを起動しメッセージを送信すると、ブラウザの更新なしで別ブラウザにメッセージが表示されることが確認できます。
本番で利用するために
デモでは以上のところまでを実装して実演しました。 本番環境で利用するためには以上に加えて最低限、以下の実装が必要になります。
認証
スタンドアローンモードで起動することにより別サーバでの起動
バックエンドのアダプターを変更
認証は言うまでもなく一般的なWebアプリケーションであれば必要になります。認証はapp/channels/application_cable/connection.rbに実装します。
スタンドアローンモードでの起動をすることにより、Web用のアプリケーションとWebSocket用のアプリケーションを分離して管理、実行することが可能になります。
バックエンドアダプターはデフォルトではasyncと呼ばれるインメモリで接続を管理する方法が選択されているため、プロセス間での接続情報をやりとりできず、ブロードキャストがうまくいきません。なのでredisのようなアダプターを選択します。
その他にもワーカ数の設定などが必要になります。 WEB+DB PRESS Vol.93「新登場Rails5」という特集に詳しく書かれています。
まとめ
ActionCableを利用することで簡単にWebSocketを組み込むことができるようになりました。
FiNCでは本番環境ですでにRails 5のアプリケーションが動いています。ActionCableは執筆時点ではまだ利用していませんが、実装を検討しています。 ActionCableを本番環境で使用したときにはあらためて本ブログにて紹介したいと思います。
How To Create a Multi-Use Channel with Rails 5 Beta ActionCable
The highlight of rails 5, still in beta, is the introduction of ActionCable, which allows browsers across multiple users to communicate information nearly instantly. Imagine, for example, a board game where each users sees the board pieces respond as soon as the other player makes a move, or a group order that updates whenever a user adds an item, or a message room where users can see chats in real time. When one user does an action, the browsers for all users will show it without the page refresh.
Before Rails 5, this critical functionality could not be achieved natively within the Rails framework. It had to be forced in using third party code. With Rails 5, however, ActionCable will be merged in to work as elegantly as any other natural Rails capability.
How is ActionCable implemented? It uses a WebSocket protocol that, on initialization with a client, creates a connection between the client and the server, constantly updating all users whenever a change is communicated to the server. We developers enact these connections by creating ‘channels’ that carry out the tasks we want the WebSocket to accomplish. Thus, to take the examples from earlier, we might have a messaging channel, an item adding channel, and a game move channel.
But, using a switch statement, we can also create a single channel that can handle many different types of requests.
In my food ordering app, we wanted to create a single channel in a restaurant order show page where users could add and delete items to an order as well as chat with other users about the order. Other users would see items added to or deleted from the order and messages added to the chat as soon as they happened.
To get this dual functionality, we decided to create a single channel that could handle both tasks. By doing so, we also avoided some Rails 5′s bugs that can occur when multiple different channels are enacted, freezing the server. Rails 5, after all, is still in beta!
Take a look at controller for items and the controller for messages. Note that each time we broadcast to the server, one of the data params we pass in has a key of :action and a string as its value that describes the action of the broadcast.
app/assets/controllers/messages_controller.rb
app/assets/controllers/items_controller.rb
Saw the :action strings on lines 9 and 12, 30 respectively? They aren’t magic. Rather, they help operate a javascript switch statement that determines the response from our channel.
See below for the javascript:
app/assets/javascripts/channels/orders.js
There are three types of actions on the order show page that we want done by the channel. The first is to make a message appear in the chat column when a user creates a message (lines 4-7). The second is for an item to appear and the cost of an order to update when a user adds an item (lines 8-12). The third is for an item to disappear and the cost of an order to update if a user deletes the item (lines 13-17).
Take a look at the switch statement on line 3. We call .action on the data sent by the channel, which gives back one of the strings we sent through as the value of the action key (either add-message, add-item, or delete-item). Based on that string, different code is run. Though not all of the code for the different cases are included in the screenshot above, you can get a general sense of what the code does from the names of the functions.
POOF. By using a switch statement and passing through a string as part of data, we’ve efficiently added multiple capabilities to our order show page through the use of a single ActionCable channel.
Welcome to Rails 5!