SocketStream - 外部ライブラリ使用方法
Socketstreamはじめました。
今回は表題について説明します。
・config/app.coffee
・enviroments/
が設定ファイルです。
$ socketstream console SocketStream > SS.config
で設定値が見れます。
configのpack_assetsがtrueだとsocketstream起動時に自動で読み込んでくれます。
というわけで
#/path/to/config/app.coffee
exports.config =
pack_assets: true
...
を追記。
次に外部ライブラリの配置場所は
lib/clients|server
下です。
説明が見つかりませんでしたが、多分1.
などファイル名の数字prefixで読み込む順番を指定します。
試しにBackbone.jsをlibに置いてみる。
$ ls client/ 1.jquery.min.js 2.jquery.tmpl.min.js 3.helpers.js 4.underscore-min.js 5.backbone-min.js
って感じで
$ socketstream s 16 Dec 05:44:53 - Pre-packing all client assets... 16 Dec 05:44:53 - Concatenating file 1.jquery.min.js 16 Dec 05:44:53 - Concatenating file 2.jquery.tmpl.min.js 16 Dec 05:44:53 - Concatenating file 3.helpers.js 16 Dec 05:44:53 - Minified 3.helpers.js from 4.439 KB to 2.1 KB 16 Dec 05:44:53 - Concatenating file 4.underscore-min.js 16 Dec 05:44:56 - Minified 4.underscore-min.js from 11.608 KB to 11.057 KB 16 Dec 05:44:56 - Concatenating file 5.backbone-min.js 16 Dec 05:44:56 - Minified 5.backbone-min.js from 13.804 KB to 13.473 KB 16 Dec 05:44:56 - Appending SocketStream client files... 16 Dec 05:44:56 - Concatenating file reset.css 16 Dec 05:44:56 - CSS libs concatenated 16 Dec 05:44:56 - Compiling and adding app/client/app.coffee 16 Dec 05:44:56 - Compiling and adding app/client/demo.coffee 16 Dec 05:44:56 - Minified application code from 1.221 KB to 0.861 KB 16 Dec 05:44:56 - Stylus files compiled into CSS 16 Dec 05:44:56 - Compiled app.jade to index.html info - socket.io started ------------------------------ SocketStream ------------------------------ Version 0.2.7 running in development on PID 5103 Primary web server listening on http://0.0.0.0:3000 Spawned 1 back end worker process (PID 5107) -------------------------------------------------------------------------- ...
おkkk。
コンパイルされてコンプレスされて自動読み込みされる/assets/lib_[\d+].jsファイルが出来上がります。
でも連番とか気持ち悪いからrailsのassets pipelineみたいな感じで読み込めないかなー
Socket.ioでクライアントオブジェクトにメソッドを追加する方法 socket.io - node.js
以下coffeescriptになります。
ちなみにsocket.ioのバージョンは0.8.7になります。
socket.io@0.8.7 ./node_modules/socket.io
├── policyfile@0.0.4
└── socket.io-client@0.8.7
channel = io.of('/channels').on 'connection', (client) -> client.hoge() # => "hoge!" Socket = require('socket.io/lib/socket.js') Socket.prototype.hoge = -> "hoge!"
以上になります。
EventMachine,Sinatraのインテグレーション - eventmachine - sinatra - routing
eventmachineでルーティングが必要になりライブラリを探していたのですが、
sinatraと組み合わせるのが一番簡単そうです。
# /path/to/emapp.rb %w( rubygems eventmachine shout sinatra/base ).each { |lib| require lib} EventMachine::run do class App < Sinatra::Base get '/' do end end App.run!(:port => 3000) end
でおkです。
時間があったらwebsocket + EventMachineのインテグレーションも紹介したいと思います。
参考
Any success with Sinatra working together with EventMachine WebSockets? - stack overflow
rails + Backbone.jsでhaml-jsをテンプレートとして使う方法- Backbone.js - rails- jammit
まずさらっと紹介。
・backbone.js
…クライアントサイドjsにMVCを提供してくれるすんげーライブラリ。railsと相性抜群(っぽい。まだサラっとしか見てません)
railsと連携してルーティングとか使ってみたいので、knockoutjs使ってましたがこっちに切り替えようかなと思ってます。
・jammit
…jsのテンプレートとかcssとかをパッケージングしてくれるライブラリ。backbone.jsのviewテンプレートについて調べてたら出てきました。
Backbone is agnostic with respect to your preferred method of HTML templating. Your render function could even munge together an HTML string, or use document.createElement to generate a DOM tree. However, we suggest choosing a nice JavaScript templating library. Mustache.js, Haml-js, and Eco are all fine alternatives. Because Underscore.js is already on the page, _.template is available, and is an excellent choice if you've already XSS-sanitized your interpolated data.
Backboneのドキュメントより
ってことで、慣れてることもありテンプレートはHaml-jsを使いたいと思います。
まずjammitインストール
# /yourapp/Gemfile gem "jammit"
でbundle
次にjammitが使うテンプレートフォルダと拡張子、javascriptをincludeする際のkeyを指定
# /yourapp/config/assets.yml embed_assets: on #拡張子の追加 template_extension: jst.haml template_function: Haml javascripts: #includeする際のkey workspace: #テンプレートフォルダとファイルマッチ - app/views/**/*.jst.haml
って感じになる思います。
あとはhttps://github.com/visionmedia/haml.js/
をダウンロードしてpublic or vendor下に配置
最後に、ヘッダで先ほど設定したjammitのキーでjsを読み込みます。
- #/yourapp/app/views/layouts/application.html.haml.or.something !!! %html %head = javascript_include_tag "/haml-js/lib/haml.js" = include_javascripts :workspace ...
以上です。
# /yourapp/app/views/some/thing.jst.haml
%h1 hello haml js world
みたいなの書くとjammit君がコンパイルしてくれて
/* /assets/workspace.jst.haml */ (function(){ window.JST = window.JST || {}; window.JST['some/thing'] = Haml('%h1 hello haml js world\n\n'); })();
こんなん生成してくれます。
参考 -
・haml-js,jammit設定方法について
・backboneでのベストなhamltemplateの使い方について - stack overflow
・knockoutjsとbackbone.jsどっちがいいかについて - stack overflow
Backbone seeks to be more a full MVC platform, whereas Knockout.js gives you a foundation to apply MVVM/MVP and go from there.
knockoutjsとbackbone.jsどっちがいいかについて より
辺りが議論呼んでそうですね。
knockoutjs1〜2週間くらい触りましたが*observable*とデータバインドの威力は絶大でした。
データを更新するとUIが自動的に書き換わるのは書いててすげー衝撃的な楽さでした。
ただMVVMに慣れていないせいかデータ定義とメソッドがごっちゃになってしまって、コード量が増えてくるとリファクタに手こずった、というのが第一の感想です。
まだMVVMのアーキテクチャへの理解が深まる前ですが、Backbone.jsも試してみたいと思います。
rails - redis - zrange with scores - ubuntu - インストール + zrange でWITHSCORESをつけるオプション
ライブラリは以下を使用
type : redis client
library : redis-rb
git : https://github.com/ezmobius/redis-rb
# /path/to/app/Gemfile gem 'redis'
でbundle install
あまり乗り気じゃないけどグローバル変数にインスタンス突っ込む。
# /path/to/app/config/initializers/redis.rb $redis = Redis.new(:host => 'localhost', :port => 6379)
で
$redis.zrange("yourzset", 0, 10,'WITHSCORES')
とかやってみたけどscoreがとれなかった。
gitのソースを見てみると
def zrange(key, start, stop, options = {}) command = CommandOptions.new(options) do |c| c.bool :withscores c.bool :with_scores end synchronize do @client.call [:zrange, key, start, stop, *command.to_a] end end
となっていたので
$redis.zrange("yourzset", 0, 10,:withscores => true)
でとれた。
node.js - socket.io - Chat Room - NameSpace - 名前空間 | emitの範囲 を分ける方法まとめ
必要なメソッド : to , join , leave, of , clients
tips1 : namespaceについてはsocket.ioのHow To Useページを参照
tips2 : toを使うとidにjoinしたメンバーだけに送られる
tips3 : clientsメソッドでroomidにjoinしているメンバーのsocketが得られる
tips4 : leaveでroomから出る
tips3については先日のnode.js - socket.io - rails - sessionを共有する方法で紹介したsessionの保存と合わせて
roomにjoinしているメンバーのhandshakeデータからセッションデータを引っ張ってこれます。
下記はcoffeescriptのソースになります。
Client
#util p = (data)-> console.log data #channel class Channel constructor : (@roomid)-> #tips1 @sock = io.connect(youriohost + '/channels') @sock.emit('join',@roomid) #tips2 @sock.on 'room message',(msg) -> p msg #*tips3 @sock.on 'members in room',(members) -> p members sendMessage : (message) -> @sock.emit('sendMessage',@roomid,message) channel = new Channel(roomid) channel.sendMessage("only roomid's member can listen")
Server
io = require('socket.io').listen(3002) connect = require('connect') util = require('util') #tips1 channel = io.of('/channels').on 'connection', (sock) -> sock.on 'join',(roomid) -> sock.join(roomid) sock.set('roomid',roomid) #*tips3 sock.emit('members in room',channel.findRoomClients(roomid)) sock.on 'sendMessage',(id,message) -> #tips2 channel.to(id).emit('room message',message) sock.on 'disconnect' , () -> sock.get 'roomid',(err,roomid) -> #tips4 sock.leave(roomid) #*tips3 channel.findRoomClients = (roomid)-> this.clients(roomid).map (client) -> client.what_you_want client.handshake.session.id client.handshake.session.name
node.js - socket.io - rails - sessionを共有する方法
railsで他言語とsessionを共有する際問題となるのが、
内部的にmarshalという形式でデータを保持しているため、
ruby以外の言語でsessionを取得しようとすると、marshalのdeserializeが出来ないとセッションデータを読み込むことが出来ません。
そのため、セッションを共有するために
・セッションデータの保持形式を marshal -> yaml or json or messagepack ....に変更
・ついでにセッションデータをクッキーではなくdb保存に変更
の2つを行います。
※今回はデータ保持にjson , セッションdbに mongodbを使います。
まずrails、node.js両者でmongoとsessionライブラリを用意。
type - orm
library - mongo_mapper
git - http://mongomapper.com/
type - session store
library - mongo_session_store
git - https://github.com/brianhempel/mongo_session_store
# /path/to/app/Gemfile gem 'mongo_mapper' gem 'bson_ext' gem 'mongo_ext' gem "mongo_session_store-rails3"
セットアップはgit参照
node.js
library - mongoose
git - https://github.com/LearnBoost/mongoose
npm install mongoose
セットアップはgit参照
次にrailsでデータ保持形式を marshal -> json に変更するためモンキーパッチを充てます。
# /path/to/app/config/initializers/session_store.rb YourApp::Application.config.session_store :mongo_mapper_store module ActionDispatch module Session class MongoMapperStore < MongoStoreBase class Session include MongoMapper::Document set_collection_name MongoSessionStore.collection_name key :_id, String key :data, String, :default => {}.to_json() timestamps! end end class MongoStore < MongoStoreBase class Session def initialize(options = { }) @_id = options[:_id] @data = options[:data] || {}.to_json @created_at = options[:created_at] @updated_at = options[:updated_at] end end end class MongoStoreBase < AbstractStore private def pack(data) data.to_json end def unpack(packed) return nil unless packed data = JSON.parse(packed) if data.has_key?('flash') data['flash'] = ActionDispatch::Flash::FlashHash.new.update(Hash[*data['flash']]) end data end end end end
変更点は
・ソースのMarshalを使っていた部分をjsonに直しただけ
・なんかエラーが出たので微修正。参考は以下
http://memo.yomukaku.net/entries/314
でrailsについてはjson形式でデータが保持されるはずです。
あとはmongooseでsessiondb読むだけです。
ついでにsocket.ioのhandshake時にsessionを書き込みます。
大体こんな感じになると思います。
※以下はcoffeescriptです
io = require('socket.io').listen(3002) connect = require('connect') sys = require('sys') util = require('util') events = require("events") mongo = require('mongoose'); mongo.connect('mongodb://localhost/your_session_db') Schema = mongo.Schema SessionSchema = new Schema _id: { type: String, required: true, unique: true } data: { type: String, default: '{}' } expires: { type: Date, index: true } Session = mongo.model('Session', SessionSchema) # config io.configure -> io.set 'authorization', (handshake,callback) -> cookie = handshake.headers.cookie sessid = parseCookie(cookie)['_yoursession_id'] Session.findOne { _id : sessid},(err,session) -> if err return callback(err,false) else handshake.session = JSON.parse(session.data) return callback(err,true) io.of('/channels').on 'connection', (sock) -> console.log sock.handshake.session
なんか不具合出るんじゃないかと不安なので、不具合出た方連絡下さい。