Ruby on Railsにリアルタイム機能を簡単に導入する方法
Ruby on RailsのWebサービスを開発していて、ちょっとリアルタイムなチャットサポートを追加したいと思ったことはないでしょうか?
Googleで「Ruby on Rails リアルタイム」と検索すると(2015年7月現在)、以下のような記事がトップにヒットします。
- Ruby on Railsとrubymotionでリアルタイムweb構築 - Qiita
- websocket-railsを使ってRailsでリアルタイムチャットを実装する方法 - インターファーム開発部ブログ
少し面倒くさそうに見える上、保存も行うとなるとまた設計が必要になってきます。
そこで今回は、Ruby on Railsで開発されている既存のアカウントシステムを利用して、Milkcocoaを使ったリアルタイムな機能を簡単に導入する方法を説明します。
どういう設計になるか
実際にどういう設計になるのか説明します。
ユーザーごとに異なるビューを表示するのですが、既存のWebサービスの設計はそのままで、表示するURLもユーザーごとに変えません。
では、ユーザーごとに何を変えるのでしょうか。
それは、Milkcocoaのデータストアです。Railsのログイン情報によって、データの取得・保存先のデータストアを変更することで、ユーザーごとにプライベートなビューを表示することができます。
具体的には、JSON Web Tokenを用いて、Railsのユーザ情報をMilkcocoaで利用します。
「Railsでのユーザ情報を、Milkcocoaで利用できる」とはどういうことでしょう。
Milkcocoaでは以下のようなセキュリティルールを記述できるのですが、例えば、この例ではmessage/syuheiというデータストアを操作できるのは、nameが"syuhei"であるユーザだけです。
セキュリティルール
message/[username] {
permit : all;
rule : account.name == username;
}Ruby on Railsで作ったアカウントシステムで、"syuhei"という名前のユーザがいる場合に、それをMilkcocoaで利用できるというのがポイントです。
実装方法の概要
実際にどうやって実装するかを説明します。
今回は、Railsの学習教材で一番有名?なMichael Hartl氏のRuby on Rails チュートリアルの例を使って、実装します。
第8章のサンプルアプリを改造して、フォローと投稿ができる、Twitterのようなものを作っています。
MilkcocoaとRuby on Railsを連携することで、リアルタイムになるだけでなく、以下のようなメリットがあります。
- Milkcocoaを利用することで、フロントエンドエンジニアだけで開発、保守ができる。
- リアルタイム通信部分のスケールアップをMilkcocoaに任せられる。
- 既存のプロジェクトのアカウントシステムのまま、Milkcocoaを導入できる。
- アカウント情報等のサービスにとって重要な情報のみを自前で持つことことができる(今回のサンプルだと、ユーザー名・パスワード・フォローしている・フォローされているといった情報。メッセージはMilkcocoaに保存される)。
以下のリポジトリにソースコードを公開しています。
以下がデモになります。
前準備(Ruby on Rails、JSON Web Tokenの導入)
では始めましょう。RubyやRuby on Railsの導入については以下の記事を参照下さい。
一応Rubyの環境を記述しておきます。
$ ruby -v
ruby 2.2.1p85 (2015-02-26 revision 49769) [x86_64-darwin13]JSON Web Tokenを使えるようにするため、Gemでjwtパッケージをインストールします。Gemfileに以下を追加して、bundle installしましょう。
Gemfile
...
gem 'jwt'
...途中でCan't find the PostgreSQL client library (libpq)でつまずいたので、こちらの記事を参考にして解決しました。
$ ARCHFLAGS="-arch x86_64" bundle install
$ cp config/database.yml.example config/database.yml
$ rake db:migrate
$ raile serverソースコードの説明
それでは、コードの説明に入ります。
書く必要があるのは、「JSON Web Tokenでトークンを作成するコントローラ側のコード」と「チャット表示するビュー側のコード(JavaScript)」です。
JSON Web Tokenでトークンを作成するコントローラ側のコード
まずはログイン処理用のトークンを作成するコードを書きます。
Railsでのユーザ情報をトークン化します。今回は簡単にするため、Rails側ではpasswordはチェックせず、好きなユーザ名でログインできるようにしています。
app/controllers/sessions_controller.rb
require 'jwt'
def get_token
exp = Time.now.to_i + 4 * 3600
print(current_user)
payload = {:id => current_user.id, :exp => exp }
hmac_secret = 'Milkcocoaのシークレットキー'
token = JWT.encode payload, hmac_secret, 'HS256'
render :json => {:token => token }
endたったこれだけで、RailsとMilkcocoaが連携できます。
ここで重要なのは'Milkcocoaのシークレットキー'のところに、Milkcocoaの管理画面にあるシークレットトークンを貼付けることです。管理画面のシークレットトークンはbase64エンコードされたもの(Auth0の場合)と、そうでないもの(AuthRocketやその他の場合)があるので、そうでないものをコピーしてください。
チャット表示するビュー側のコード
次にビューです。
トークン化したユーザ情報をmilkcocoa.authWithTokenでMilkcocoaに渡しています。これでRails側でログインしたユーザと同じユーザ名でMilkcocoaのアプリにログインしたことになります。ログインしている状態で以下を実行すると、ユーザ情報が取得できます。
milkcocoa.user(function(err, user) {console.log(user)});ここではapp_id.mlkcca.comの部分を、自分のMilkcocoaアプリ用に書き換えてください。
app/views/sample/index.html.erb
$(function() {
var milkcocoa = new MilkCocoa("app_id.mlkcca.com");
milkcocoa.user(function(err, user) {
if(user) {
start(user);
}else{
loginToMilkcocoa();
}
});
//コントローラで作成したトークンを取ってきて、Milkcocoaでログインする。
function loginToMilkcocoa() {
$.get("/get_token", {}, function(data){
milkcocoa.authWithToken(data.token, function(err, user) {
start(user);
});
}, 'json');
}
// フォローしているユーザーを取得
function get_followings() {
$.get("/followings", {}, function(data){
if(data && data.followings)
data.followings.forEach(showFeed);
}, 'json');
}
function start(current_user) {
console.log(current_user);
// データストアの作成
var ds = milkcocoa.dataStore("micropost").child(current_user.id);
get_followings();
$("#post-btn").click(function(e) {
var content = $("#micropost_content").val();
console.log(content);
if(content) {
// データストアに保存
ds.push({
content : content
});
$("#micropost_content").val("");
}
});
}
function showFeed(user) {
var user_id = user.id;
var ds = milkcocoa.dataStore("micropost").child(user_id);
$("#followings").append('<div class="microposts"><div class="user">'+user.name+'</div><div id="feed-'+user_id+'"></div></div>');
// データストアのデータを取得
ds.stream().next(function(err, posts) {
posts.forEach(function(p) {
$('#feed-'+user_id).append('<div class="content">'+p.value.content+'</div>');
});
});
// pushを監視
ds.on('push', function(pushed) {
$('#feed-'+user_id).append('<div class="content">'+pushed.value.content+'</div>');
});
}
})Milkcocoaの管理画面で、セキュリティルールを追加します。
セキュリティルール
micropost/[userid] {
permit : push;
rule : account.id == userid;
}
micropost/[userid] {
permit : query, on(push)
rule : true;
}これで完成です。rails serverで確認してみてください。
もう一度デモを貼っておきます。 https://milkcocoa-rails-example.herokuapp.com/
応用すればWebサイトのお問い合わせチャットができます
上記のセキュリティルールでは、その人のフィードにはその人しか書き込めませんが、閲覧側は誰でも閲覧することが可能です。 簡単な応用として、Webサイトのお問い合わせチャットのようなものを考えてみましょう。
IDが1のsyuheiさんが全てのユーザメッセージを閲覧でき、その他のユーザは自分のフィードにしか書き込みができないようにするには、どのようなセキュリティルールにしたら良いでしょう?
以下が答えになります。
セキュリティルール
micropost/[userid] {
permit : push;
rule : account.id == userid;
}
micropost/[userid] {
permit : query, on(push)
rule : account.id == userid;
}
micropost/[userid] {
permit : query, on(push)
rule : account.id == "1";
}セキュリティルールを上記のように書くと、syuheiさんは、以下のようにURLのhashから閲覧したいユーザーのチャットを選択できるようにできます。
var other_username = location.hash.substr(1);//escapeすべき
var ds = milkcocoa.dataStore("message").child(other_username);今回の記事は、主にRuby on Railsを使ってWebサービスを作ってる方向けですが、まだ触っていない方もこの機会に触ってみてはどうでしょうか。
記事内で次の画像を加工・使用しています: "Ruby on Rails logo" by KSEltar - Own work. Licensed under CC BY-SA 3.0 via Wikimedia Commons.