SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

サンプルコードで学ぶRuby on Rails 5実践入門

Rails 5の目玉機能「Action Cable」で双方向通信を実装(1)

サンプルコードで学ぶRuby on Rails 5実践入門 第7回

  • X ポスト
  • このエントリーをはてなブックマークに追加

開発するチャットアプリ(2)

チャット用チャネルの作成

 いよいよチャネルの作成です。Rails 5より新たに追加されたジェネレートコマンドである、rails g channelを使って作成します。第1引数には、チャネル名を指定します。ここでは、コントローラー・モデル名と合わせてchat_messageを指定します。第2引数には、チャネルに自動で追加したいメソッドを指定します。ここでは発言する意味のspeakメソッドとするため、speakを指定します。

bin/rails g channel chat_message speak

 ↓

Running via Spring preloader in process 18493
      create  app/channels/chat_message_channel.rb
   identical  app/assets/javascripts/cable.js
      create  app/assets/javascripts/channels/chat_message.coffee

 実行結果から分かる通り、サーバー側のチャネルファイルとクライアント側のJavaScript、CoffeeScriptが自動生成されます。まずは自動生成されたクライアント側のJavaScriptコードを確認しましょう。

リスト1 app/assets/javascripts/cable.js
…(中略)…
//= require action_cable
//= require_self
//= require_tree ./channels

(function() {
  this.App || (this.App = {});

  App.cable = ActionCable.createConsumer();

}).call(this);

 「cable.js」は、クライアントからサーバーに対してWebSocket接続しているコードです。App.cable = ActionCable.createConsumer();でWebSocket通信を確立しています。

 次に、自動生成されたCoffeeScriptコードにチャットで「発言する」動作を定義するspeakメソッドを、リスト2の通りに修正します。

リスト2 app/assets/javascripts/channels/chat_message.coffee
App.chat_message = App.cable.subscriptions.create "ChatMessageChannel",
…(中略)…
  speak: (message) ->
    @perform 'speak', message: message

 App.chat_messageの定義の1行目で、Action Cableのサーバー側のチャネルをcreateしています。createの引数にChatMessageChannelが指定されているので、「app/channels/chat_message_channel.rb」で指定されるサーバー側のチャネルにクライアント側から接続します。

 デフォルトのspeakメソッドを書き換え、引数に指定されたmessageをサーバー側のチャネルのspeakメソッドに引数として渡すようにします。@perform 'speak', message: messageと記述すると、ChatMessageChannelspeakメソッドを呼び出すことができます。このCoffeeScriptコードを定義したspeakメソッドをクライアントから呼び出すには、App.chat_message.speak(発言メッセージ)とします。これでクライアント側から発言メッセージをサーバー側に送ることができます。

 今度はサーバー側のChatMessageChannelspeakメソッドの動作定義を入れます。リスト3の通りにspeakメソッドを修正しましょう。

リスト3 app/channels/chat_message_channel.rb
…(中略)…
class ChatMessageChannel < ApplicationCable::Channel
  def subscribed
    stream_from 'chat_message_channel'
  end
…(中略)…
  def speak(data)
    ActionCable.server.broadcast 'chat_message_channel', message: data['message']
  end
end

 speakメソッドはクライアント側で、発言メッセージを指定したキーワード引数を取るように定義しました。サーバー側では引数に指定したdata経由で、data['message']とすることで発言メッセージを取り出すことができます。

 speakメソッドのActionCable.server.broadcastでは第1引数にチャネル名を、第2引数に発言メッセージを指定することで、サーバー側からChatMessageChannelにWebSocketで接続している全クライアントに対して発言メッセージを配信することができます。

 なおsubscribedメソッドは、各クライアントに配信する内容をどこに配信するかを定義しています。この機能はストリームと呼ばれ、Railsが提供するstream_fromメソッドを通じて、発言メッセージを'chat_message_channel'に接続したクライアントに配信できるようになります。

 実はたったこれだけで、/chat_messages/indexにアクセスしてWebSocketを使う最低限の準備が整います。書いたコードは数行なので、Action CableによるWebSocket通信の実現が簡単なことが実感できるのではないでしょうか。この時点でアプリケーションログを確認すると、WebSocket通信が開始されている様子が分かります。

$ cat log/development.log

 ↓

Started GET "/chat_messages/index" for ::1 at 2017-03-22 18:16:38 +0900
Processing by ChatMessagesController#index as HTML
  Rendering chat_messages/index.html.erb within layouts/application
  Rendered chat_messages/index.html.erb within layouts/application (1.3ms)
Completed 200 OK in 254ms (Views: 242.2ms | ActiveRecord: 0.0ms)


Finished "/cable/" [WebSocket] for ::1 at 2017-03-22 18:16:38 +0900
ChatMessageChannel stopped streaming from chat_message_channel
Started GET "/cable" for ::1 at 2017-03-22 18:16:38 +0900
Started GET "/cable/" [WebSocket] for ::1 at 2017-03-22 18:16:38 +0900
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
ChatMessageChannel is transmitting the subscription confirmation
ChatMessageChannel is streaming from chat_message_channel

 チャネルクラスであるChatMessageChannelの上記のようなログが出力されていることが確認できれば、WebSocket通信が成功しています。ただ、現段階で/chat_messages/indexにアクセスしても、発言メッセージを投稿する入力フォームがありませんので作成しましょう。

入力フォームの作成

 発言を入力するフォームをリスト4の通りに追加します。

リスト4 app/views/chat_messages/index.html.erb
<div id='chat_messages'>
</div>
<br/>
<form>
  <label>
    発言する: <input type="text" data-behavior="speak_chat_messages">
  </label>
</form>

 div要素のidchat_messagesとなっている部分には、発言した内容が入ります。現状、まだ発言メッセージをDBに保存していないので空のままにしておきます。また、エンターキーを押下すると発言メッセージがサーバー側に送信される動作定義を後で入れるため、data-behavior属性を定義しておきます。なお、data-behavior属性はhtml5の独自データ属性で、アプリケーションの動作とCSSの分離のために使います。

 次に、発言する入力フォームでエンターキーが入力されたらサーバー側に発言メッセージを送信するCoffeeScriptのコードをリスト5の通りに記述します。

リスト5 app/assets/javascripts/channels/chat_message.coffee
…(中略)…
  speak: (message) ->
    @perform 'speak', message: message

$(document).on 'keypress', '[data-behavior~=speak_chat_messages]', (event) ->
  if event.keyCode is 13
    App.chat_message.speak event.target.value
    event.target.value = ''
    event.preventDefault()

 keypressイベントは、ブラウザ上でキーボードからの入力を拾います。data-behavior属性がspeak_chat_messagesの場合としているので、先ほど指定した入力フォームでのキーボード入力イベントを拾っています。

 引数に指定されたeventを使ってevent.keyCodeプロパティにアクセスすることで、キーボードの入力文字に対応した値を取得できます。keyCode13(エンターキー)の場合に、サーバー側に発言メッセージを送信する処理を呼び出します。発言メッセージを送信した後は入力フォームを空にし、最後にpreventDefault()でイベントをキャンセルすることで、エンターキーを入力してもフォームが送信されないようにしておきます。

 最後に、サーバー側チャネルのspeakメソッドでブロードキャスト(broadcast)された発言メッセージを、クライアント側で受け取ってブラウザ上に表示する実装を追加します。

リスト6 app/assets/javascripts/channels/chat_message.coffee
App.chat_message = App.cable.subscriptions.create "ChatMessageChannel",
…(中略)…
  received: (data) ->
    $('#chat_messages').append '<div>' + data['message'] + '</div>'
…(中略)…

 サーバー側のチャネルからブロードキャストされた発言メッセージをクライアント側で受け取る処理は、receivedメソッドに記述します。サーバー側から送られてきたデータを引数dataで受け取ります。発言メッセージはdata['message']で取り出すことができます。事前にビューファイルで指定したid=chat_messagesdiv要素に、発言メッセージのdivを新たに追加しています。

次のページ
開発するチャットアプリ(3)

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
サンプルコードで学ぶRuby on Rails 5実践入門連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 竹馬 力(チクバ ツトム)

WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/10153 2017/05/17 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング