亀の速度で走る

"Computer Programming is the closest thing we have to a superpower."

Websocket Railsを使って特定のユーザーにメッセージを送る

特定のユーザにメッセージを送る

前回のブログ記事でWebsocket Railsを用いて接続が確立したユーザ全員にメッセージを送る実装方法について書きました。今回はFacebookのプライベートメッセージのように特定のユーザにメッセージを送りたい時にどうしたら良いのかを書いていきたいと思います。(この記事では省略していますが、参考サイトに載せているGithubのページのInstallation Guildeに従ってWebsocket Railsのインストールをして下さい。または前回の記事を見て下さい。)

参考サイト

前回同様Websocket Railsを使っていきます。今回も本家のサイトこちらのサイトを参考にしました。

特定のユーザの指定方法

さて、どうやって実装したらいいのかな。と少し調べていたら、本家のサイトにこのような事が書いてありました。ふむふむ。UserManagerとやらを使うらしい。

You can send an event to a specific client using the UserManager which can be accessed via WebsocketRails.users.

myUser.idとは何かな?WebsocketRails.usersとは?

WebsocketRails.users[myUser.id].send_message('new_notification', {:message => 'you\'ve got an upvote '})


それは二個目の参考サイトにこのように書いてありました。

If there is a `current_user` method defined in ApplicationController and a user is signed in to your application when the connection is opened, WebsocketRails will call the method defined in `config.user_identifier` on the `current_user` object and use that value as the key.If no `current_user` method is defined or the user is not signed in when the WebsocketRails connection is opened, the connection will not be stored in the UserManager.

なるほど。application controllerにcurrent_userメソッドを定義しておけばWebsocketの接続が確立されたと同時にUserManagerにユーザの情報を格納してくれるらしい。便利ですなー。

実装方法

もう既に出来上がっているサイトで、ごちゃごちゃしていてどこから説明していいのか分からないので、必要最低限の部分をメモ程度に書いていこうと思います。既にユーザなどは存在する体でお願いします。ちなみにUIはこのような形です。画像があった方が少しはイメージし易いかもしれません!

f:id:takuyanz:20150130210124p:plain


まず参考サイトに書いてあったようにapplication_controllerに current_userを定義します。これがないと始まらない。

def current_user
 return unless session[:user_id]
 @current_user ||= User.find(session[:user_id])
end


前回のブログ同様JS側でWebsocketの接続を確立させるコード、Websocketを通してサーバー側から送られてきたメッセージを表示するコードを書きます。実際に書いたコードをかなり簡略化させています。

$(document).ready(function(e){
  var ws_rails = new WebSocketRails("localhost:3000/websocket");
 
  ws_rails.bind("websocket_message", function(message){
  //json形式で送られてくるのでパースします
   var data = JSON.parse(message);
 
  //データをゴニョゴニョしてHtmlを生成して、appendします
   $("#message_box").append(ゴニョゴニョされたやつ);
  });
});


これでほとんど完成です。後はコントローラー側でメッセージがCreateされた時に以下のコード書きます。

message = "message"
WebsocketRails.users[送りたいユーザーのID].send_message('websocket_message', message)


下記がメッセージがcreateされた時の実際のコードになります。少し汚いかもしれません。リーダブルコード読み直すので許して下さい。

 def create
    #これらの情報はajaxで送られてきています
    msg_to_id = params.require(:message_to_id)
    message_text = params.require(:message_text)


    # 新しくmessageを作ります。message_from_idは今のユーザIdを格納
    message = Message.new(message_to_id: msg_to_id, message_from_id: current_user.id, text: message_text)

    if message.save
   #JS側でHtmlを生成する為の情報を変数に格納します
   #message:メッセージの内容 image_url:送り主のプロフィール画像 nickname:送り主の名前 message_id:メッセージのID created_at: 時間
      message_data = {message: message_text, image_url: current_user.image_url, nickname: current_user.nickname, message_id: message.id, created_at: message.created_at.strftime("%X %d/%m")}.to_json

      # Websocketを用いて今メッセージを送ったユーザと送られたユーザーにメッセージを送ります。
      # 同時に二人は出来ないっぽい
      WebsocketRails.users[msg_to_id].send_message(:websocket_message, message_data)
      WebsocketRails.users[current_user.id].send_message(:websocket_message, message_data)
     
      render nothing: true
     else
      render status: 500
     end
  end


これで動くと思います。
因みにbinding.pryでWebsocketRails.users(UserManager)の中身を見てみるとこんな感じです。ちゃんとユーザのデータが格納されています。

[1] pry(#<MessagesController>)> WebsocketRails.users
=> #<WebsocketRails::UserManager:0x000000091e7180
 @users=
  {"2"=>
    #<WebsocketRails::UserManager::LocalConnection:0x000000091e70e0
     @connections=
      [#<Connection::23c23305067da6ce6f36>,
       #<Connection::49eb23fad9a1acfd2154>,
       #<Connection::94af81b53170778b8d91>,
       #<Connection::df220cd8d761665985bc>,
       #<Connection::b26cc68e6842a8cff094>,
       #<Connection::dd73e630844a776c05a9>]>,
   "1"=>
    #<WebsocketRails::UserManager::LocalConnection:0x00000007d3d0e0[f:id:takuyanz:20150130230126p:plain]
     @connections=[#<Connection::88238ad7c31d08b3dc8d>, #<Connection::611d13d332b53cf85051>, #<Connection::946088f8474222d7640d>]>}>


Websocketの接続を確立させる時に以下のコードよりは、

var ws_rails = new WebSocketRails("localhost:3000/websocket");

実際にサイトをWeb上に上げる事を考えて以下のように書くと、ベターかも。

var ws_rails = new WebSocketRails(location.host + "/websocket");


この方法で以下のような通知機能や、新しいコンテンツがある場合のプラス+1なども実装する事が出来ました。

f:id:takuyanz:20150130230126p:plain


f:id:takuyanz:20150130230121p:plain


説明が少し分かりにくかったかな?技術系の記事を書いた事がないので、これから書きながら慣れていきたいと思います。

Websocketってすごいな。とにかく感動しまくりでした。Webアプリケーションを作りたいとプログラミングを始めて5か月が経とうとしています。サイトが裏でどのような動きをしているのかが分かってきて、わくわくしております。わくわくわくわく。

:)