発言にユーザーIDを保存してユーザー名を表示する
最後に、発言と併せてユーザーIDもデータベースに保存するようにします。そのためには、クライアント側とサーバー側(チャネル)でコネクションごとにユーザーを識別できるようにする必要があります。また、データベースに発言を保存する処理とブロードキャスト後にクライアント側で発言を表示する処理に、ユーザー名を追加する必要があります。
コネクションの修正
まず、Action Cableのコネクションを確立する段階でユーザーを識別できるようにするため、リスト10の通りにコネクションを修正します。
module ApplicationCable class Connection < ActionCable::Connection::Base identified_by :current_user def connect self.current_user = find_verified_user logger.add_tags 'ActionCable', current_user.name end protected def find_verified_user if verified_user = User.find_by(id: cookies.signed[:user_id]) verified_user else reject_unauthorized_connection end end end end
identified_by
メソッドは、WebSocketのコネクションを識別するために提供されており、current_user
を識別子として指定しています。
接続するクライアント側のユーザーIDを取得するには、find_verified_user
メソッド内のコードの通り、ブラウザのクッキーを用います。ログイン時点で、サーバー側からクッキーをクライアント側に発行した、暗号化されたuser_id
をcookies.signed[:user_id]
として取得して、ユーザーIDがデータベースに登録されている場合は、current_user
にセットされ、これを識別子として用います。ユーザーIDがデータベースに登録されていない場合には、reject_unauthorized_connection
メソッドが呼び出されます。
reject_unauthorized_connection
メソッドは、WebSocket通信を拒否するために提供されているメソッドです。
セッションヘルパーの修正
セッションヘルパーのlog_in
メソッドで、暗号化されたユーザーIDをクッキーとして発行するコードをリスト11の通りに追加しましょう。
module SessionsHelper …(中略)… def log_in(user) callback = session[:callback] reset_session session[:user_id] = user.id session[:callback] = callback cookies.permanent.signed[:user_id] = user.id end …(中略)…
コードのpermanent
は、発行したクッキーを永続化するためのものです。また、signed
を指定することでクッキーに格納されるユーザーIDが暗号化されます。
チャネルの修正
チャネルのspeak
メソッドでモデルを生成する箇所に、ユーザーIDをリスト12の通りに追加します。
class ChatMessageChannel < ApplicationCable::Channel …(中略)… def speak(data) ChatMessage.create! user_id: current_user.id, body: data['message'] end end
モデルの修正
ChatMessage、Userモデルの関連とビュー側で使用するuser_name
メソッドを、リスト13、リスト14の通りに追加します。
class ChatMessage < ApplicationRecord …(中略)… belongs_to :user def user_name return '名無しさん' if user_id.blank? user.name end end
class User < ApplicationRecord …(中略)… has_many :chat_messages end
ChatMessageモデルのuser_name
メソッドは、これまでのテストでchat_messages
テーブルのuser_id
にNullのデータが入っているための措置で、user_id
が空だった場合にはユーザー名を「名無しさん」と表示するようにしています。
ジョブの修正
ジョブでブロードキャストする処理に、ユーザー名をリスト15の通り追加します。
class ChatMessageBroadcastJob < ApplicationJob …(中略)… def perform(chat_message) ActionCable.server.broadcast 'chat_message_channel', user_name: chat_message.user_name, message: chat_message.body end end
ユーザー名の取得には、ChatMessageモデルに定義したuser_name
メソッドを使っています。
CoffeeScriptの修正
ブロードキャストされた場合、クライアント側で発言を受け取ってhtmlとして表示する箇所を、リスト16の通りに修正します。
…(中略)… received: (data) -> $('#chat_messages').append '<div>' + data['user_name'] + ': ' + data['message'] + '</div>'
data['user_name']
でブロードキャストされた発言のユーザー名を取得しています。
ビューの修正
最後にブロードキャストした場合と同様、リスト17の通り発言の表示にユーザー名を加えましょう。
<div id='chat_messages'> <% @chat_messages.each do |chat_message| %> <div><%= "#{chat_message.user_name}: #{chat_message.body}" %></div> <% end %> </div>
動作確認
これで、発言にユーザーIDを保存してユーザー名を表示する修正が完了しました。「http://localhost:3000/chat_messages/index
」にアクセスして動作確認をしてみましょう。
図1は、左側がChromeでtest user1としてログインしたもの、右側がSafariでtest user2としてログインして動作確認した結果です。
まとめ
今回は、前回に引き続きAction Cableのサンプルアプリを拡張して、発言をデータベースに保存できるようにし、会員登録機能と関連付けを行いました。
連載最終回の次回は、Rails 5のもう1つの目玉機能であるAPIモードで別のRailsアプリケーションを立ち上げ、今回開発したチャットアプリに機能を追加します。