Cable」を用いた、ライブチャットアプリの実装手順を解説したいと思います。Ajaxの非同期通信と自動更新機能を同時に実装することでもできますが、今回はWebSocket通信を用いてLINEやSlack風のチャットアプリを作成します。これから実装するライブチャットアプリは、Rails生みの親DHH氏が実際に作成したものです。この記事はDHH氏の実装を翻訳+Rails6.0系にアレンジしたものになります。元ネタは以下のYouTube動画をご覧ください。Cableの実装に取りかかる前に、まずは理解しておいてほしい通信システムを解説します。Cableを実装することで、スムーズに理解できるのでぜひ目を通しておきましょう。Cableはここに該当するHTTP通信HTTP通信とは、WebブラウザとWebサーバーの間でHTMLや画像ファイルなどのコンテンツの送受信に用いられる通信プロトコルです。Webページを閲覧・利用できるのは、HTTPという仕組みがあるためです。HTTP通信ではサーバーに対してリクエスト(例えばページの更新)を送らない限り、クライアントが持っている情報が更新されることはありません。Ajax通信Ajax通信とは、JavaScriptを使って非同期でサーバーとやり取りをする通信のことです。ブラウザとサーバの通信とは別に、JavaScriptが通信を行うため、ページ遷移を行わずに情報の更新をすることができます。Ajax通信は裏でリクエストを送ってJavaScriptで書き換えているだけのため、リアルタイムなやり取りをするには常に裏でやり取りをしなければなりません。WebSocket通信WebSocket通信とは、サーバー側とユーザー側を常時接続状態にしておき、双方向通信ができるようにする技術です。クライアントがリクエストを送信する必要がないため、リアルタイムで情報を更新することができます。CableはWebSocketのフレームワークになります。Cableとは、Rails5から導入されたリアルタイム通信技術です。これにより、ユーザーはページを更新せずとも最新の情報を取得することができます。ここで1つの疑問点が。それは、リアルタイム通信技術はAjax通信でも実装できるのではないか?ということです。先程解説した通り、Ajax通信は裏でリクエストを送ってJavaScriptで書き換えているだけのため、リアルタイムなやり取りをするには常に裏でやり取りをしなければなりません。Ajaxの自動更新機能を実装することで、一見リアルタイムで情報の更新が行われているように見えますが、実際は通信が走っています。Cable)を用いるとこれら2つの機能(非同期通信機能と自動更新機能)を同時に実装できることから、簡易なリアルタイムチャットアプリの実装に用いられています。今回以下のようなリアルタイムチャットアプリを実装していきます。上記GIFをクリックすると別タブで見ることができます。次にDHH氏が実装したリアルタイムチャットアプリと今回実装するアプリケーションの違いを見ていきます。異なる点は以下になるので、これらを頭に入れた上で実装に取り掛かりましょう。今回の実装Rails6.0系で開発クライアントサイドの実装としてJavaScriptを使用データベースにMySQLを使用チャットアプリの実装手順では早速、リアルタイムチャットアプリの実装に移ります。DHH氏は20分程で実装しているため、実際にかかる時間は20〜25分前後を想定しています。DHH氏が実装しているチャットアプリとこれから実装するチャットアプリの仕様が異なるため、実装手順に若干の違いが生じている点に注意してください。アプリケーションの立ち上げまずは以下のコマンドを入力し、campfireという名前のアプリケーションを生成しましょう。「–skip-spring」オプションはDHH氏が実装している段階(リリース前のβ版)では必要ですが、現在は必要ないので打たなくても構いません。続いてRoomsコントローラーを作成しましょう。show「routes.rb」を編集し、Roomsコントローラーのshowアクションがトップページになるように定義します。server」を打ち込んだ後「localhost:3000」にアクセスし、以下のようなビューが表示されるか確かめましょう。次にtext型のcontentカラムを持ったMessageモデルを作成します。db:migrate」を行いましょう。続いてはビューの編集です。投稿されたメッセージを一覧表示するため、Messageの配列をインスタンス変数@messagesに渡しましょう。endend部分テンプレートとして、views以下にmessagesフォルダを配置、さらにその配下に「_message.html.erb」を作成します。%></p></div>「views/rooms/show.html.erb」を以下のように編集し、先ほど作成した部分テンプレートが呼び出されるように記述しましょう。@messages」の省略形です。曖昧な方は以下の記事を参考にしてください。【Rails基礎】ややこしい部分テンプレートの省略形について簡単にまとめてみた部分テンプレートの省略形について簡単にまとめています。部分テンプレートに渡すインスタンス変数が、複数形の時と単数形の場合で記述方法が異なるので、この機会に記述方法を整理してみてください。console」を用いてテストデータを登録します。World!)が表示されるか確認しましょう。フォームの作成DHH氏の動画では後半にフォームを作成していますが、今回はここで投稿フォームを作成します。以下のようにviews/rooms/show.html.erbを編集しましょう。data-behavior="room_speaker"></form>このようなフォームが作成されていれば成功です。Cableの設定に入っていきます。以下のコマンドを打ち込み、speakメソッドを持つRoomチャネルを作成しましょう。以下のような、チャネルの関連ファイルが複数生成されるはずです。Cable専用のファイルと思っていただいて構いません。チャネル自体はMVCモデルのコントローラーと役割が似ています。生成されたファイルの内、「room_channel.rb」がサーバーサイドの処理を担うチャネル、「room_channel.js」がクライアントサイドの処理を担うチャネルになります。上記2つのファイル以外は編集しないので、無視していただいて構いません。作成された「room_channel.rb」と「room_channel.js」の中身をそれぞれ見てみると、以下のようになっています。‘/cable’」を追記しましょう。'rooms#show'end現在のバージョンでは記述しなくても同様の挙動を示します。続いて、サーバーサイドの処理を受け持つapp/channels/room_channel.rbを編集します。data[‘message’]」の記述を追記しましょう。endend順番に解説していきます。subscribedは直訳すると「購読する」という意味で、クライアントがサーバーに接続したと同時に実行されるメソッドです。unsubscribedでは反対に、クライアントの接続が解除されたと同時に実行されるメソッドです。Cableが保持しているメソッドの1つで、データの送受信を行うことができます。今回の場合で言うと、room_channelを宣言することによって、room_channel間(room_channel.rbとroom_channel.js)でデータを送受信することができます。"room_channel"end次は以下の記述です。これはroom_channel.jsで実行されたspeakのメッセージを受け取り、room_channel.jsのreceivedメソッドにブロードキャスト(送信)する記述です。端的に言うと、room_channel.jsのreceivedメソッドにdata[‘message’]を送信しています。次にクライアントサイド側の処理を受け持つapp/javascripts/channels/room_channel.jsを編集しましょう。今回はテキストボックスに文字が入力されエンターキーが押された際、アラートが表示されるように実装します。}})まずは以下の記述です。13」はEnterキーが押下されたことを表し、「appRoom.speak(e.target.value)」でroom_channel.jsのspeakアクションを発火させています。”;」「e.preventDefault();」が作動します。}})「e.target.value」でテキストボックスに打ち込んだ文字列を取得することができます。実際に取得できているかどうかは、「console.log(e.target.value)」を記述し、コンソール上で確かめてみましょう。「e.preventDefault」によって、ブラウザが持っているデフォルトの動作を妨げてくれます。詳しくはこちらの記事を参考にしてください。「event.keyCode」で押したキーのキーコードを取得することができます。取得できるイベントはkeyup、keydown、keypressの3つです。キーコード一覧は以下のサイトを参考にしてください。続いて以下の記述です。function(message)のmessageが仮引数、実引数は先ほど定義した「e.target.value」になります。room_channel.rbのspeakアクションを動かすために、中でspeak関数を定義しています。加えて、引数messageとして「e.target.value」をroom_channel.rbのspeakアクションに送信しています。message});}最後は以下の記述です。room_channel.rbでブロードキャスト(送信)されたデータがreceivedに届き、アラート表示を実行しています。アラート表示する内容は「data([‘message’])」ですが、これは「e.target.value」で取得したデータと同じになっています。alert(data['message']);},挙動確認1ここで一度挙動を確認しておきます。文字を入力しエンターキーを押すと、アラート表示が出てくるのか確認しましょう。ここまでのチャネルの流れを図解どのような流れでアラート表示まで行われているのか、改めて確認しましょう。実装の続きに戻ります。次は、入力したテキストがデータベースに保存されるように実装します。room_channel.rbの記述を以下のように編集しましょう。data['message']end続いて、保存後の処理の記述を追記します。Messageモデルに以下の記述を追記しましょう。この記述があることで、データ保存後の処理を指定することができます。今回は、データ保存後にMessageBroadcastJobのperformメソッドを実行するように記述します。引数はself}」となるね。先ほどspeakメソッドのブロードキャスト処理を削除してしまったため、別ファイルで定義し直さなければなりません。加えて、データ保存後はMessageBroadcastJobを呼び出すことから、MessageBroadcastJobを新たに作成しなければなりません。以下のコマンドを打ち込み、ブロードキャストのjobを作成しましょう。作成されたファイルの内、今回使用するのはapp/jobs/message_broadcast_job.rbのみです。JobはRails4.2から追加された機能で、メール送信や請求書発行などをバックグラウンドで実行することが可能になります。ActiveJobを利用することで、重たい処理(=時間のかかる処理)を非同期的に実行することができるようになります。作成した「app/jobs/message_broadcast_job.rb」を以下のように編集しましょう。endend順番に見ていきましょう。以下の記述でブロードキャスト処理を定義しています。render_message(message)を呼び出しています。render_message(message)end次は以下の記述です。})endここでは部分テンプレートである「app/views/messages/_message.html.erb」を呼び出しています。「app/views/messages/_message.html.erb」の記述は、以下のような形でレンダリングされています。class='message'><p>投稿したテキスト</p></div>"}「ApplicationController.renderer」を使用することで、コントローラーのアクションの制約を受けずに、任意のビューファイルをレンダリングできます。ブラウザ内の記述を書き換えるいよいよ最後の実装です。最後は打ち込んだテキストが、そのままビューに反映されるように実装します。room_channel.jsのreceivedの記述を以下のように編集しましょう。data['message']);},引数dataの中身を「console.log(data)」で確認すると、以下のようになっています。ここからmessageのみを取得し、insertAdjacentHTMLメソッドを使用することで、打ち込んだテキストがテキストボックス上に積み重なるように表示させます。挙動確認2ここまでで実装が全て終了しました。最後に挙動を確認し、以下と同じになることを確かめましょう。総合まとめ最後にまとめとして、ビューが表示されるまでのコードの流れを図解しました。初めは複雑で理解するのに苦労するとは思いますが、何度もアプリケーションを作り直し、理解に落とし込んでいきましょう。ユーザーがサーバーを立ち上げ、localhost:3000にアクセスするとsubscribeアクションが発火する。stream_fromメソッドによりクライアントサイド側のroom_channel.jsとサーバーサイド側のroom_channel.rbでデータの送受信ができるようになります。テキストを入力し、エンターキーを押下するとイベントが発火する。同ファイルのspeakアクションが呼び出される。room_channel.rbのspeakメソッドを呼び出し、入力したテキストをデータベースに保存する。after_create_commitメソッドにより、データベースに保存後、message_broadcast_job.rbのperformアクションが呼び出される。render_message(message)の記述により、private以下の記述が呼び出される。部分テンプレートの記述を呼び出す。broadcastを介して、room_channel.jsのreceivedにデータが渡される。渡されたデータのテキスト情報だけを抽出し、ブラウザに表示させる。Cable」を用いて、ライブチャットアプリを実装してみました。簡単とは言えない実装ですが、何度も作り直してみることで大まかな流れは理解できると思います。チャレンジしてみてください。Insightsでサイトスピードを確認...Railsエンジニアちばよしはる銀座にあるヘルステック企業のRailsエンジニア。初心者から中級者向けに記事を更新しています。お酒とカレーと競馬が好き。TechTechMedia。