faye 0.6.2 → 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of faye might be problematic. Click here for more details.
- data/History.txt +13 -0
- data/README.rdoc +4 -5
- data/lib/faye-browser-min.js +1 -1
- data/lib/faye.rb +1 -2
- data/lib/faye/adapters/rack_adapter.rb +0 -1
- data/lib/faye/engines/base.rb +0 -1
- data/lib/faye/engines/memory.rb +5 -4
- data/lib/faye/engines/redis.rb +77 -34
- data/lib/faye/protocol/client.rb +15 -9
- data/lib/faye/protocol/server.rb +8 -5
- data/lib/faye/transport/local.rb +4 -0
- data/spec/browser.html +8 -0
- data/spec/install.sh +33 -8
- data/spec/javascript/client_spec.js +5 -4
- data/spec/javascript/engine_spec.js +1 -1
- data/spec/javascript/server/extensions_spec.js +2 -2
- data/spec/javascript/server_spec.js +10 -11
- data/spec/phantom.js +17 -0
- data/spec/ruby/server/extensions_spec.rb +2 -3
- data/spec/ruby/server_spec.rb +9 -10
- metadata +36 -2
data/History.txt
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
=== 0.6.3 / 2011-07-10
|
2
|
+
TestSwarm build: http://swarm.jcoglan.com/job/69/
|
3
|
+
|
4
|
+
* Use sequential message IDs to reduce memory usage on the client side
|
5
|
+
* Only send advice with handshake and connect responses
|
6
|
+
* Stop trying to publish /meta/* messages - no-one is listening and it breaks /**
|
7
|
+
* Fix bug causing invalid listeners to appear after a client reconnection
|
8
|
+
* Stop loading 'rubygems' within our library code
|
9
|
+
* Make sure we only queue a message for each client once in the Redis engine
|
10
|
+
* Use lists instead of sets for message queues in Redis
|
11
|
+
* Improve clean-up of expired clients in Redis engine
|
12
|
+
|
13
|
+
|
1
14
|
=== 0.6.2 / 2011-06-19
|
2
15
|
|
3
16
|
* Add authentication, database selection and namespacing to Redis engine
|
data/README.rdoc
CHANGED
@@ -46,13 +46,12 @@ should get you up and running:
|
|
46
46
|
bundle exec rspec -c spec/
|
47
47
|
node spec/node.js
|
48
48
|
|
49
|
-
#
|
49
|
+
# Install Ruby gem
|
50
50
|
gem build faye.gemspec
|
51
|
-
gem install faye-
|
51
|
+
gem install faye-x.x.x.gem
|
52
52
|
|
53
|
-
#
|
54
|
-
|
55
|
-
npm install ./faye-0.6.0.tgz
|
53
|
+
# Install NPM package
|
54
|
+
npm install build
|
56
55
|
|
57
56
|
|
58
57
|
== To-do
|
data/lib/faye-browser-min.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
if(!this.Faye)Faye={};Faye.extend=function(a,b,c){if(!b)return a;for(var d in b){if(!b.hasOwnProperty(d))continue;if(a.hasOwnProperty(d)&&c===false)continue;if(a[d]!==b[d])a[d]=b[d]}return a};Faye.extend(Faye,{VERSION:'0.6.2',BAYEUX_VERSION:'1.0',ID_LENGTH:128,JSONP_CALLBACK:'jsonpcallback',CONNECTION_TYPES:['long-polling','cross-origin-long-polling','callback-polling','websocket','in-process'],MANDATORY_CONNECTION_TYPES:['long-polling','callback-polling','in-process'],ENV:(function(){return this})(),random:function(a){a=a||this.ID_LENGTH;if(a>32){var b=Math.ceil(a/32),c='';while(b--)c+=this.random(32);return c}var d=Math.pow(2,a)-1,f=d.toString(36).length,c=Math.floor(Math.random()*d).toString(36);while(c.length<f)c='0'+c;return c},commonElement:function(a,b){for(var c=0,d=a.length;c<d;c++){if(this.indexOf(b,a[c])!==-1)return a[c]}return null},indexOf:function(a,b){for(var c=0,d=a.length;c<d;c++){if(a[c]===b)return c}return-1},each:function(a,b,c){if(a instanceof Array){for(var d=0,f=a.length;d<f;d++){if(a[d]!==undefined)b.call(c||null,a[d],d)}}else{for(var g in a){if(a.hasOwnProperty(g))b.call(c||null,g,a[g])}}},map:function(a,b,c){var d=[];this.each(a,function(){d.push(b.apply(c||null,arguments))});return d},filter:function(a,b,c){var d=[];this.each(a,function(){if(b.apply(c,arguments))d.push(arguments[0])});return d},size:function(a){var b=0;this.each(a,function(){b+=1});return b},enumEqual:function(c,d){if(d instanceof Array){if(!(c instanceof Array))return false;var f=c.length;if(f!==d.length)return false;while(f--){if(c[f]!==d[f])return false}return true}else{if(!(c instanceof Object))return false;if(this.size(d)!==this.size(c))return false;var g=true;this.each(c,function(a,b){g=g&&(d[a]===b)});return g}},asyncEach:function(a,b,c,d){var f=a.length,g=-1,h=0,i=false;var j=function(){h-=1;g+=1;if(g===f)return c&&c.call(d);b(a[g],m)};var k=function(){if(i)return;i=true;while(h>0)j();i=false};var m=function(){h+=1;k()};m()},toJSON:function(a){if(this.stringify)return this.stringify(a,function(key,value){return(this[key]instanceof Array)?this[key]:value});return JSON.stringify(a)},timestamp:function(){var b=new Date(),c=b.getFullYear(),d=b.getMonth()+1,f=b.getDate(),g=b.getHours(),h=b.getMinutes(),i=b.getSeconds();var j=function(a){return a<10?'0'+a:String(a)};return j(c)+'-'+j(d)+'-'+j(f)+' '+j(g)+':'+j(h)+':'+j(i)}});Faye.Class=function(a,b){if(typeof a!=='function'){b=a;a=Object}var c=function(){if(!this.initialize)return this;return this.initialize.apply(this,arguments)||this};var d=function(){};d.prototype=a.prototype;c.prototype=new d();Faye.extend(c.prototype,b);return c};Faye.Namespace=Faye.Class({initialize:function(){this._c={}},exists:function(a){return this._c.hasOwnProperty(a)},generate:function(){var a=Faye.random();while(this._c.hasOwnProperty(a))a=Faye.random();return this._c[a]=a},release:function(a){delete this._c[a]}});Faye.Error=Faye.Class({initialize:function(a,b,c){this.code=a;this.params=Array.prototype.slice.call(b);this.message=c},toString:function(){return this.code+':'+this.params.join(',')+':'+this.message}});Faye.Error.parse=function(a){a=a||'';if(!Faye.Grammar.ERROR.test(a))return new this(null,[],a);var b=a.split(':'),c=parseInt(b[0]),d=b[1].split(','),a=b[2];return new this(c,d,a)};Faye.Error.versionMismatch=function(){return new this(300,arguments,"Version mismatch").toString()};Faye.Error.conntypeMismatch=function(){return new this(301,arguments,"Connection types not supported").toString()};Faye.Error.extMismatch=function(){return new this(302,arguments,"Extension mismatch").toString()};Faye.Error.badRequest=function(){return new this(400,arguments,"Bad request").toString()};Faye.Error.clientUnknown=function(){return new this(401,arguments,"Unknown client").toString()};Faye.Error.parameterMissing=function(){return new this(402,arguments,"Missing required parameter").toString()};Faye.Error.channelForbidden=function(){return new this(403,arguments,"Forbidden channel").toString()};Faye.Error.channelUnknown=function(){return new this(404,arguments,"Unknown channel").toString()};Faye.Error.channelInvalid=function(){return new this(405,arguments,"Invalid channel").toString()};Faye.Error.extUnknown=function(){return new this(406,arguments,"Unknown extension").toString()};Faye.Error.publishFailed=function(){return new this(407,arguments,"Failed to publish").toString()};Faye.Error.serverError=function(){return new this(500,arguments,"Internal server error").toString()};Faye.Deferrable={callback:function(a,b){if(!a)return;if(this._s==='succeeded')return a.apply(b,this._i);this._j=this._j||[];this._j.push([a,b])},errback:function(a,b){if(!a)return;if(this._s==='failed')return a.apply(b,this._i);this._k=this._k||[];this._k.push([a,b])},setDeferredStatus:function(){var a=Array.prototype.slice.call(arguments),b=a.shift(),c;this._s=b;this._i=a;if(b==='succeeded')c=this._j;else if(b==='failed')c=this._k;if(!c)return;var d;while(d=c.shift())d[0].apply(d[1],this._i)}};Faye.Publisher={countSubscribers:function(a){if(!this._3||!this._3[a])return 0;return this._3[a].length},addSubscriber:function(a,b,c){this._3=this._3||{};var d=this._3[a]=this._3[a]||[];d.push([b,c])},removeSubscriber:function(a,b,c){if(!this._3||!this._3[a])return;if(!b){delete this._3[a];return}var d=this._3[a],f=d.length;while(f--){if(b!==d[f][0])continue;if(c&&d[f][1]!==c)continue;d.splice(f,1)}},removeSubscribers:function(){this._3={}},publishEvent:function(){var b=Array.prototype.slice.call(arguments),c=b.shift();if(!this._3||!this._3[c])return;Faye.each(this._3[c],function(a){a[0].apply(a[1],b)})}};Faye.Timeouts={addTimeout:function(a,b,c,d){this._4=this._4||{};if(this._4.hasOwnProperty(a))return;var f=this;this._4[a]=Faye.ENV.setTimeout(function(){delete f._4[a];c.call(d)},1000*b)},removeTimeout:function(a){this._4=this._4||{};var b=this._4[a];if(!b)return;clearTimeout(b);delete this._4[a]}};Faye.Logging={LOG_LEVELS:{error:3,warn:2,info:1,debug:0},logLevel:'error',log:function(a,b){if(!Faye.logger)return;var c=Faye.Logging.LOG_LEVELS;if(c[Faye.Logging.logLevel]>c[b])return;var a=Array.prototype.slice.apply(a),d=' ['+b.toUpperCase()+'] [Faye',f=this.className,g=a.shift().replace(/\?/g,function(){try{return Faye.toJSON(a.shift())}catch(e){return'[Object]'}});for(var h in Faye){if(f)continue;if(typeof Faye[h]!=='function')continue;if(this instanceof Faye[h])f=h}if(f)d+='.'+f;d+='] ';Faye.logger(Faye.timestamp()+d+g)}};Faye.each(Faye.Logging.LOG_LEVELS,function(a,b){Faye.Logging[a]=function(){this.log(arguments,a)}});Faye.Grammar={LOWALPHA:/^[a-z]$/,UPALPHA:/^[A-Z]$/,ALPHA:/^([a-z]|[A-Z])$/,DIGIT:/^[0-9]$/,ALPHANUM:/^(([a-z]|[A-Z])|[0-9])$/,MARK:/^(\-|\_|\!|\~|\(|\)|\$|\@)$/,STRING:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*$/,TOKEN:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+$/,INTEGER:/^([0-9])+$/,CHANNEL_SEGMENT:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+$/,CHANNEL_SEGMENTS:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,CHANNEL_NAME:/^\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,WILD_CARD:/^\*{1,2}$/,CHANNEL_PATTERN:/^(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*\/\*{1,2}$/,VERSION_ELEMENT:/^(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*$/,VERSION:/^([0-9])+(\.(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*)*$/,CLIENT_ID:/^((([a-z]|[A-Z])|[0-9]))+$/,ID:/^((([a-z]|[A-Z])|[0-9]))+$/,ERROR_MESSAGE:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*$/,ERROR_ARGS:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*$/,ERROR_CODE:/^[0-9][0-9][0-9]$/,ERROR:/^([0-9][0-9][0-9]:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*|[0-9][0-9][0-9]::(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)$/};Faye.Extensible={addExtension:function(a){this._5=this._5||[];this._5.push(a);if(a.added)a.added()},removeExtension:function(a){if(!this._5)return;var b=this._5.length;while(b--){if(this._5[b]!==a)continue;this._5.splice(b,1);if(a.removed)a.removed()}},pipeThroughExtensions:function(c,d,f,g){this.debug('Passing through ? extensions: ?',c,d);if(!this._5)return f.call(g,d);var h=this._5.slice();var i=function(a){if(!a)return f.call(g,a);var b=h.shift();if(!b)return f.call(g,a);if(b[c])b[c](a,i);else i(a)};i(d)}};Faye.extend(Faye.Extensible,Faye.Logging);Faye.Channel=Faye.Class({initialize:function(a){this.id=this.name=a},push:function(a){this.publishEvent('message',a)},isUnused:function(){return this.countSubscribers('message')===0}});Faye.extend(Faye.Channel.prototype,Faye.Publisher);Faye.extend(Faye.Channel,{HANDSHAKE:'/meta/handshake',CONNECT:'/meta/connect',SUBSCRIBE:'/meta/subscribe',UNSUBSCRIBE:'/meta/unsubscribe',DISCONNECT:'/meta/disconnect',META:'meta',SERVICE:'service',expand:function(a){var b=this.parse(a),c=['/**',a];var d=b.slice();d[d.length-1]='*';c.push(this.unparse(d));for(var f=1,g=b.length;f<g;f++){d=b.slice(0,f);d.push('**');c.push(this.unparse(d))}return c},isValid:function(a){return Faye.Grammar.CHANNEL_NAME.test(a)||Faye.Grammar.CHANNEL_PATTERN.test(a)},parse:function(a){if(!this.isValid(a))return null;return a.split('/').slice(1)},unparse:function(a){return'/'+a.join('/')},isMeta:function(a){var b=this.parse(a);return b?(b[0]===this.META):null},isService:function(a){var b=this.parse(a);return b?(b[0]===this.SERVICE):null},isSubscribable:function(a){if(!this.isValid(a))return null;return!this.isMeta(a)&&!this.isService(a)},Set:Faye.Class({initialize:function(){this._2={}},getKeys:function(){var c=[];Faye.each(this._2,function(a,b){c.push(a)});return c},remove:function(a){delete this._2[a]},hasSubscription:function(a){return this._2.hasOwnProperty(a)},subscribe:function(c,d,f){if(!d)return;Faye.each(c,function(a){var b=this._2[a]=this._2[a]||new Faye.Channel(a);b.addSubscriber('message',d,f)},this)},unsubscribe:function(a,b,c){var d=this._2[a];if(!d)return false;d.removeSubscriber('message',b,c);if(d.isUnused()){this.remove(a);return true}else{return false}},distributeMessage:function(c){var d=Faye.Channel.expand(c.channel);Faye.each(d,function(a){var b=this._2[a];if(b)b.publishEvent('message',c.data)},this)}})});Faye.Subscription=Faye.Class({initialize:function(a,b,c,d){this._8=a;this._2=b;this._l=c;this._m=d;this._t=false},cancel:function(){if(this._t)return;this._8.unsubscribe(this._2,this._l,this._m);this._t=true},unsubscribe:function(){this.cancel()}});Faye.extend(Faye.Subscription.prototype,Faye.Deferrable);Faye.Client=Faye.Class({UNCONNECTED:1,CONNECTING:2,CONNECTED:3,DISCONNECTED:4,HANDSHAKE:'handshake',RETRY:'retry',NONE:'none',CONNECTION_TIMEOUT:60.0,DEFAULT_ENDPOINT:'/bayeux',INTERVAL:0.0,initialize:function(b,c){this.info('New client created for ?',b);this.endpoint=b||this.DEFAULT_ENDPOINT;this._u=c||{};Faye.Transport.get(this,Faye.MANDATORY_CONNECTION_TYPES,function(a){this._d=a},this);this._1=this.UNCONNECTED;this._2=new Faye.Channel.Set();this._y=new Faye.Namespace();this._n={};this._6={reconnect:this.RETRY,interval:1000*(this._u.interval||this.INTERVAL),timeout:1000*(this._u.timeout||this.CONNECTION_TIMEOUT)};if(Faye.Event)Faye.Event.on(Faye.ENV,'beforeunload',this.disconnect,this)},getClientId:function(){return this._0},getState:function(){switch(this._1){case this.UNCONNECTED:return'UNCONNECTED';case this.CONNECTING:return'CONNECTING';case this.CONNECTED:return'CONNECTED';case this.DISCONNECTED:return'DISCONNECTED'}},handshake:function(c,d){if(this._6.reconnect===this.NONE)return;if(this._1!==this.UNCONNECTED)return;this._1=this.CONNECTING;var f=this;this.info('Initiating handshake with ?',this.endpoint);this._9({channel:Faye.Channel.HANDSHAKE,version:Faye.BAYEUX_VERSION,supportedConnectionTypes:[this._d.connectionType]},function(b){if(b.successful){this._1=this.CONNECTED;this._0=b.clientId;Faye.Transport.get(this,b.supportedConnectionTypes,function(a){this._d=a},this);this.info('Handshake successful: ?',this._0);this.subscribe(this._2.getKeys(),true);if(c)c.call(d)}else{this.info('Handshake unsuccessful');Faye.ENV.setTimeout(function(){f.handshake(c,d)},this._6.interval);this._1=this.UNCONNECTED}},this)},connect:function(a,b){if(this._6.reconnect===this.NONE)return;if(this._1===this.DISCONNECTED)return;if(this._1===this.UNCONNECTED)return this.handshake(function(){this.connect(a,b)},this);this.callback(a,b);if(this._1!==this.CONNECTED)return;this.info('Calling deferred actions for ?',this._0);this.setDeferredStatus('succeeded');this.setDeferredStatus('deferred');if(this._o)return;this._o=true;this.info('Initiating connection for ?',this._0);this._9({channel:Faye.Channel.CONNECT,clientId:this._0,connectionType:this._d.connectionType},this._v,this)},disconnect:function(){if(this._1!==this.CONNECTED)return;this._1=this.DISCONNECTED;this.info('Disconnecting ?',this._0);this._9({channel:Faye.Channel.DISCONNECT,clientId:this._0});this.info('Clearing channel listeners for ?',this._0);this._2=new Faye.Channel.Set()},subscribe:function(c,d,f){if(c instanceof Array)return Faye.each(c,function(channel){this.subscribe(channel,d,f)},this);var g=new Faye.Subscription(this,c,d,f);var h=(d===true);if(!h&&this._2.hasSubscription(c)){this._2.subscribe([c],d,f);g.setDeferredStatus('succeeded');return g}this.connect(function(){this.info('Client ? attempting to subscribe to ?',this._0,c);this._9({channel:Faye.Channel.SUBSCRIBE,clientId:this._0,subscription:c},function(a){if(!a.successful)return g.setDeferredStatus('failed',Faye.Error.parse(a.error));var b=[].concat(a.subscription);this.info('Subscription acknowledged for ? to ?',this._0,b);this._2.subscribe(b,d,f);g.setDeferredStatus('succeeded')},this)},this);return g},unsubscribe:function(c,d,f){if(c instanceof Array)return Faye.each(c,function(channel){this.unsubscribe(channel,d,f)},this);var g=this._2.unsubscribe(c,d,f);if(!g)return;this.connect(function(){this.info('Client ? attempting to unsubscribe from ?',this._0,c);this._9({channel:Faye.Channel.UNSUBSCRIBE,clientId:this._0,subscription:c},function(a){if(!a.successful)return;var b=[].concat(a.subscription);this.info('Unsubscription acknowledged for ? from ?',this._0,b)},this)},this)},publish:function(a,b){if(!Faye.Grammar.CHANNEL_NAME.test(a))throw new Error("Cannot publish: '"+a+"' is not a valid channel name");this.connect(function(){this.info('Client ? queueing published message to ?: ?',this._0,a,b);this._9({channel:a,data:b,clientId:this._0})},this)},receiveMessage:function(c){this.pipeThroughExtensions('incoming',c,function(a){if(!a)return;if(a.advice)this._z(a.advice);var b=this._n[a.id];if(b){delete this._n[a.id];b[0].call(b[1],a)}this._A(a)},this)},_9:function(b,c,d){b.id=this._y.generate();if(c)this._n[b.id]=[c,d];this.pipeThroughExtensions('outgoing',b,function(a){if(!a)return;this._d.send(a,this._6.timeout/1000)},this)},_z:function(a){Faye.extend(this._6,a);if(this._6.reconnect===this.HANDSHAKE&&this._1!==this.DISCONNECTED){this._1=this.UNCONNECTED;this._0=null;this._v()}},_A:function(a){if(!a.channel||!a.data)return;this.info('Client ? calling listeners for ? with ?',this._0,a.channel,a.data);this._2.distributeMessage(a)},_B:function(){if(!this._o)return;this._o=null;this.info('Closed connection for ?',this._0)},_v:function(){this._B();var a=this;Faye.ENV.setTimeout(function(){a.connect()},this._6.interval)}});Faye.extend(Faye.Client.prototype,Faye.Deferrable);Faye.extend(Faye.Client.prototype,Faye.Logging);Faye.extend(Faye.Client.prototype,Faye.Extensible);Faye.Transport=Faye.extend(Faye.Class({MAX_DELAY:0.0,batching:true,initialize:function(a,b){this.debug('Created new ? transport for ?',this.connectionType,b);this._8=a;this._a=b;this._e=[]},send:function(a,b){this.debug('Client ? sending message to ?: ?',this._8._0,this._a,a);if(!this.batching)return this.request([a],b);this._e.push(a);this._7=b;if(a.channel===Faye.Channel.HANDSHAKE)return this.flush();if(a.channel===Faye.Channel.CONNECT)this._p=a;this.addTimeout('publish',this.MAX_DELAY,this.flush,this)},flush:function(){this.removeTimeout('publish');if(this._e.length>1&&this._p)this._p.advice={timeout:0};this.request(this._e,this._7);this._p=null;this._e=[]},receive:function(a){this.debug('Client ? received from ?: ?',this._8._0,this._a,a);Faye.each(a,this._8.receiveMessage,this._8)},retry:function(a,b){var c=this;return function(){Faye.ENV.setTimeout(function(){c.request(a,2*b)},1000*b)}}}),{get:function(g,h,i,j){var k=g.endpoint;if(h===undefined)h=this.supportedConnectionTypes();Faye.asyncEach(this._q,function(b,c){var d=b[0],f=b[1];if(Faye.indexOf(h,d)<0)return c();f.isUsable(k,function(a){if(a)i.call(j,new f(g,k));else c()})},function(){throw new Error('Could not find a usable connection type for '+k);})},register:function(a,b){this._q.push([a,b]);b.prototype.connectionType=a},_q:[],supportedConnectionTypes:function(){return Faye.map(this._q,function(a){return a[0]})}});Faye.extend(Faye.Transport.prototype,Faye.Logging);Faye.extend(Faye.Transport.prototype,Faye.Timeouts);Faye.Event={_f:[],on:function(a,b,c,d){var f=function(){c.call(d)};if(a.addEventListener)a.addEventListener(b,f,false);else a.attachEvent('on'+b,f);this._f.push({_g:a,_r:b,_l:c,_m:d,_w:f})},detach:function(a,b,c,d){var f=this._f.length,g;while(f--){g=this._f[f];if((a&&a!==g._g)||(b&&b!==g._r)||(c&&c!==g._l)||(d&&d!==g._m))continue;if(g._g.removeEventListener)g._g.removeEventListener(g._r,g._w,false);else g._g.detachEvent('on'+g._r,g._w);this._f.splice(f,1);g=null}}};Faye.Event.on(Faye.ENV,'unload',Faye.Event.detach,Faye.Event);Faye.URI=Faye.extend(Faye.Class({queryString:function(){var c=[],d;Faye.each(this.params,function(a,b){c.push(encodeURIComponent(a)+'='+encodeURIComponent(b))});return c.join('&')},isLocal:function(){var a=Faye.URI.parse(Faye.ENV.location.href);var b=(a.hostname!==this.hostname)||(a.port!==this.port)||(a.protocol!==this.protocol);return!b},toURL:function(){var a=this.queryString();return this.protocol+this.hostname+':'+this.port+this.pathname+(a?'?'+a:'')}}),{parse:function(d,f){if(typeof d!=='string')return d;var g=new this();var h=function(b,c){d=d.replace(c,function(a){if(a)g[b]=a;return''})};h('protocol',/^https?\:\/+/);h('hostname',/^[^\/\:]+/);h('port',/^:[0-9]+/);Faye.extend(g,{protocol:'http://',hostname:Faye.ENV.location.hostname,port:Faye.ENV.location.port},false);if(!g.port)g.port=(g.protocol==='https://')?'443':'80';g.port=g.port.replace(/\D/g,'');var i=d.split('?'),j=i.shift(),k=i.join('?'),m=k?k.split('&'):[],o=m.length,l={};while(o--){i=m[o].split('=');l[decodeURIComponent(i[0]||'')]=decodeURIComponent(i[1]||'')}if(typeof f==='object')Faye.extend(l,f);g.pathname=j;g.params=l;return g}});if(!this.JSON){JSON={}}(function(){function k(a){return a<10?'0'+a:a}if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(a){return this.getUTCFullYear()+'-'+k(this.getUTCMonth()+1)+'-'+k(this.getUTCDate())+'T'+k(this.getUTCHours())+':'+k(this.getUTCMinutes())+':'+k(this.getUTCSeconds())+'Z'};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(a){return this.valueOf()}}var m=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,o=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,l,p,s={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},n;function r(c){o.lastIndex=0;return o.test(c)?'"'+c.replace(o,function(a){var b=s[a];return typeof b==='string'?b:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+c+'"'}function q(a,b){var c,d,f,g,h=l,i,j=b[a];if(j&&typeof j==='object'&&typeof j.toJSON==='function'){j=j.toJSON(a)}if(typeof n==='function'){j=n.call(b,a,j)}switch(typeof j){case'string':return r(j);case'number':return isFinite(j)?String(j):'null';case'boolean':case'null':return String(j);case'object':if(!j){return'null'}l+=p;i=[];if(Object.prototype.toString.apply(j)==='[object Array]'){g=j.length;for(c=0;c<g;c+=1){i[c]=q(c,j)||'null'}f=i.length===0?'[]':l?'[\n'+l+i.join(',\n'+l)+'\n'+h+']':'['+i.join(',')+']';l=h;return f}if(n&&typeof n==='object'){g=n.length;for(c=0;c<g;c+=1){d=n[c];if(typeof d==='string'){f=q(d,j);if(f){i.push(r(d)+(l?': ':':')+f)}}}}else{for(d in j){if(Object.hasOwnProperty.call(j,d)){f=q(d,j);if(f){i.push(r(d)+(l?': ':':')+f)}}}}f=i.length===0?'{}':l?'{\n'+l+i.join(',\n'+l)+'\n'+h+'}':'{'+i.join(',')+'}';l=h;return f}}Faye.stringify=function(a,b,c){var d;l='';p='';if(typeof c==='number'){for(d=0;d<c;d+=1){p+=' '}}else if(typeof c==='string'){p=c}n=b;if(b&&typeof b!=='function'&&(typeof b!=='object'||typeof b.length!=='number')){throw new Error('JSON.stringify');}return q('',{'':a})};if(typeof JSON.stringify!=='function'){JSON.stringify=Faye.stringify}if(typeof JSON.parse!=='function'){JSON.parse=function(g,h){var i;function j(a,b){var c,d,f=a[b];if(f&&typeof f==='object'){for(c in f){if(Object.hasOwnProperty.call(f,c)){d=j(f,c);if(d!==undefined){f[c]=d}else{delete f[c]}}}}return h.call(a,b,f)}m.lastIndex=0;if(m.test(g)){g=g.replace(m,function(a){return'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(g.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){i=eval('('+g+')');return typeof h==='function'?j({'':i},''):i}throw new SyntaxError('JSON.parse');}}}());Faye.Transport.WebSocket=Faye.extend(Faye.Class(Faye.Transport,{UNCONNECTED:1,CONNECTING:2,CONNECTED:3,batching:false,request:function(b,c){this._7=this._7||c;this._h=this._h||{};Faye.each(b,function(a){this._h[a.id]=a},this);this.withSocket(function(a){a.send(Faye.toJSON(b))})},withSocket:function(a,b){this.callback(a,b);this.connect()},connect:function(){this._1=this._1||this.UNCONNECTED;if(this._1!==this.UNCONNECTED)return;this._1=this.CONNECTING;this._b=new WebSocket(Faye.Transport.WebSocket.getSocketUrl(this._a));var d=this;this._b.onopen=function(){delete d._7;d._1=d.CONNECTED;d.setDeferredStatus('succeeded',d._b)};this._b.onmessage=function(b){var c=[].concat(JSON.parse(b.data));Faye.each(c,function(a){delete d._h[a.id]});d.receive(c)};this._b.onclose=function(){var a=(d._1===d.CONNECTED);d.setDeferredStatus('deferred');d._1=d.UNCONNECTED;delete d._b;if(a)return d.resend();Faye.ENV.setTimeout(function(){d.connect()},1000*d._7);d._7=d._7*2}},resend:function(){var c=Faye.map(this._h,function(a,b){return b});this.request(c)}}),{WEBSOCKET_TIMEOUT:1000,getSocketUrl:function(a){return Faye.URI.parse(a).toURL().replace(/^http(s?):/ig,'ws$1:')},isUsable:function(a,b,c){if(!Faye.ENV.WebSocket)return b.call(c,false);var d=false,f=false,g=this.getSocketUrl(a),h=new WebSocket(g);h.onopen=function(){d=true;h.close();b.call(c,true);f=true;h=null};var i=function(){if(!f&&!d)b.call(c,false);f=true};h.onclose=h.onerror=i;Faye.ENV.setTimeout(i,this.WEBSOCKET_TIMEOUT)}});Faye.extend(Faye.Transport.WebSocket.prototype,Faye.Deferrable);Faye.Transport.register('websocket',Faye.Transport.WebSocket);Faye.Transport.XHR=Faye.extend(Faye.Class(Faye.Transport,{request:function(b,c){var d=this.retry(b,c),f=Faye.URI.parse(this._a).pathname,g=this,h=Faye.ENV.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();h.open('POST',f,true);h.setRequestHeader('Content-Type','application/json');h.setRequestHeader('X-Requested-With','XMLHttpRequest');h.onreadystatechange=function(){if(h.readyState!==4)return;var a=h.status;try{if((a>=200&&a<300)||a===304||a===1223)g.receive(JSON.parse(h.responseText));else d()}catch(e){d()}finally{Faye.Event.detach(Faye.ENV,'beforeunload',i);h.onreadystatechange=function(){};h=null}};var i=function(){h.abort()};Faye.Event.on(Faye.ENV,'beforeunload',i);h.send(Faye.toJSON(b))}}),{isUsable:function(a,b,c){b.call(c,Faye.URI.parse(a).isLocal())}});Faye.Transport.register('long-polling',Faye.Transport.XHR);Faye.Transport.CORS=Faye.extend(Faye.Class(Faye.Transport,{request:function(a,b){var c=Faye.ENV.XDomainRequest?XDomainRequest:XMLHttpRequest,d=new c(),f=this.retry(a,b),g=this;d.open('POST',this._a,true);d.onload=function(){try{g.receive(JSON.parse(d.responseText))}catch(e){f()}finally{d.onload=d.onerror=null;d=null}};d.onerror=f;d.onprogress=function(){};d.send('message='+encodeURIComponent(Faye.toJSON(a)))}}),{isUsable:function(a,b,c){if(Faye.URI.parse(a).isLocal())return b.call(c,false);if(Faye.ENV.XDomainRequest)return b.call(c,true);if(Faye.ENV.XMLHttpRequest){var d=new Faye.ENV.XMLHttpRequest();return b.call(c,d.withCredentials!==undefined)}return b.call(c,false)}});Faye.Transport.register('cross-origin-long-polling',Faye.Transport.CORS);Faye.Transport.JSONP=Faye.extend(Faye.Class(Faye.Transport,{request:function(b,c){var d={message:Faye.toJSON(b)},f=document.getElementsByTagName('head')[0],g=document.createElement('script'),h=Faye.Transport.JSONP.getCallbackName(),i=Faye.URI.parse(this._a,d),j=this;var k=function(){if(!g.parentNode)return false;g.parentNode.removeChild(g);return true};Faye.ENV[h]=function(a){Faye.ENV[h]=undefined;try{delete Faye.ENV[h]}catch(e){}if(!k())return;j.receive(a)};Faye.ENV.setTimeout(function(){if(!Faye.ENV[h])return;k();j.request(b,2*c)},1000*c);i.params.jsonp=h;g.type='text/javascript';g.src=i.toURL();f.appendChild(g)}}),{_x:0,getCallbackName:function(){this._x+=1;return'__jsonp'+this._x+'__'},isUsable:function(a,b,c){b.call(c,true)}});Faye.Transport.register('callback-polling',Faye.Transport.JSONP);
|
1
|
+
if(!this.Faye)Faye={};Faye.extend=function(a,b,c){if(!b)return a;for(var d in b){if(!b.hasOwnProperty(d))continue;if(a.hasOwnProperty(d)&&c===false)continue;if(a[d]!==b[d])a[d]=b[d]}return a};Faye.extend(Faye,{VERSION:'0.6.3',BAYEUX_VERSION:'1.0',ID_LENGTH:128,JSONP_CALLBACK:'jsonpcallback',CONNECTION_TYPES:['long-polling','cross-origin-long-polling','callback-polling','websocket','in-process'],MANDATORY_CONNECTION_TYPES:['long-polling','callback-polling','in-process'],ENV:(function(){return this})(),random:function(a){a=a||this.ID_LENGTH;if(a>32){var b=Math.ceil(a/32),c='';while(b--)c+=this.random(32);return c}var d=Math.pow(2,a)-1,f=d.toString(36).length,c=Math.floor(Math.random()*d).toString(36);while(c.length<f)c='0'+c;return c},commonElement:function(a,b){for(var c=0,d=a.length;c<d;c++){if(this.indexOf(b,a[c])!==-1)return a[c]}return null},indexOf:function(a,b){for(var c=0,d=a.length;c<d;c++){if(a[c]===b)return c}return-1},each:function(a,b,c){if(a instanceof Array){for(var d=0,f=a.length;d<f;d++){if(a[d]!==undefined)b.call(c||null,a[d],d)}}else{for(var g in a){if(a.hasOwnProperty(g))b.call(c||null,g,a[g])}}},map:function(a,b,c){var d=[];this.each(a,function(){d.push(b.apply(c||null,arguments))});return d},filter:function(a,b,c){var d=[];this.each(a,function(){if(b.apply(c,arguments))d.push(arguments[0])});return d},size:function(a){var b=0;this.each(a,function(){b+=1});return b},enumEqual:function(c,d){if(d instanceof Array){if(!(c instanceof Array))return false;var f=c.length;if(f!==d.length)return false;while(f--){if(c[f]!==d[f])return false}return true}else{if(!(c instanceof Object))return false;if(this.size(d)!==this.size(c))return false;var g=true;this.each(c,function(a,b){g=g&&(d[a]===b)});return g}},asyncEach:function(a,b,c,d){var f=a.length,g=-1,h=0,i=false;var j=function(){h-=1;g+=1;if(g===f)return c&&c.call(d);b(a[g],m)};var k=function(){if(i)return;i=true;while(h>0)j();i=false};var m=function(){h+=1;k()};m()},toJSON:function(a){if(this.stringify)return this.stringify(a,function(key,value){return(this[key]instanceof Array)?this[key]:value});return JSON.stringify(a)},timestamp:function(){var b=new Date(),c=b.getFullYear(),d=b.getMonth()+1,f=b.getDate(),g=b.getHours(),h=b.getMinutes(),i=b.getSeconds();var j=function(a){return a<10?'0'+a:String(a)};return j(c)+'-'+j(d)+'-'+j(f)+' '+j(g)+':'+j(h)+':'+j(i)}});Faye.Class=function(a,b){if(typeof a!=='function'){b=a;a=Object}var c=function(){if(!this.initialize)return this;return this.initialize.apply(this,arguments)||this};var d=function(){};d.prototype=a.prototype;c.prototype=new d();Faye.extend(c.prototype,b);return c};Faye.Namespace=Faye.Class({initialize:function(){this._c={}},exists:function(a){return this._c.hasOwnProperty(a)},generate:function(){var a=Faye.random();while(this._c.hasOwnProperty(a))a=Faye.random();return this._c[a]=a},release:function(a){delete this._c[a]}});Faye.Error=Faye.Class({initialize:function(a,b,c){this.code=a;this.params=Array.prototype.slice.call(b);this.message=c},toString:function(){return this.code+':'+this.params.join(',')+':'+this.message}});Faye.Error.parse=function(a){a=a||'';if(!Faye.Grammar.ERROR.test(a))return new this(null,[],a);var b=a.split(':'),c=parseInt(b[0]),d=b[1].split(','),a=b[2];return new this(c,d,a)};Faye.Error.versionMismatch=function(){return new this(300,arguments,"Version mismatch").toString()};Faye.Error.conntypeMismatch=function(){return new this(301,arguments,"Connection types not supported").toString()};Faye.Error.extMismatch=function(){return new this(302,arguments,"Extension mismatch").toString()};Faye.Error.badRequest=function(){return new this(400,arguments,"Bad request").toString()};Faye.Error.clientUnknown=function(){return new this(401,arguments,"Unknown client").toString()};Faye.Error.parameterMissing=function(){return new this(402,arguments,"Missing required parameter").toString()};Faye.Error.channelForbidden=function(){return new this(403,arguments,"Forbidden channel").toString()};Faye.Error.channelUnknown=function(){return new this(404,arguments,"Unknown channel").toString()};Faye.Error.channelInvalid=function(){return new this(405,arguments,"Invalid channel").toString()};Faye.Error.extUnknown=function(){return new this(406,arguments,"Unknown extension").toString()};Faye.Error.publishFailed=function(){return new this(407,arguments,"Failed to publish").toString()};Faye.Error.serverError=function(){return new this(500,arguments,"Internal server error").toString()};Faye.Deferrable={callback:function(a,b){if(!a)return;if(this._t==='succeeded')return a.apply(b,this._j);this._k=this._k||[];this._k.push([a,b])},errback:function(a,b){if(!a)return;if(this._t==='failed')return a.apply(b,this._j);this._l=this._l||[];this._l.push([a,b])},setDeferredStatus:function(){var a=Array.prototype.slice.call(arguments),b=a.shift(),c;this._t=b;this._j=a;if(b==='succeeded')c=this._k;else if(b==='failed')c=this._l;if(!c)return;var d;while(d=c.shift())d[0].apply(d[1],this._j)}};Faye.Publisher={countSubscribers:function(a){if(!this._3||!this._3[a])return 0;return this._3[a].length},addSubscriber:function(a,b,c){this._3=this._3||{};var d=this._3[a]=this._3[a]||[];d.push([b,c])},removeSubscriber:function(a,b,c){if(!this._3||!this._3[a])return;if(!b){delete this._3[a];return}var d=this._3[a],f=d.length;while(f--){if(b!==d[f][0])continue;if(c&&d[f][1]!==c)continue;d.splice(f,1)}},removeSubscribers:function(){this._3={}},publishEvent:function(){var b=Array.prototype.slice.call(arguments),c=b.shift();if(!this._3||!this._3[c])return;Faye.each(this._3[c],function(a){a[0].apply(a[1],b)})}};Faye.Timeouts={addTimeout:function(a,b,c,d){this._4=this._4||{};if(this._4.hasOwnProperty(a))return;var f=this;this._4[a]=Faye.ENV.setTimeout(function(){delete f._4[a];c.call(d)},1000*b)},removeTimeout:function(a){this._4=this._4||{};var b=this._4[a];if(!b)return;clearTimeout(b);delete this._4[a]}};Faye.Logging={LOG_LEVELS:{error:3,warn:2,info:1,debug:0},logLevel:'error',log:function(a,b){if(!Faye.logger)return;var c=Faye.Logging.LOG_LEVELS;if(c[Faye.Logging.logLevel]>c[b])return;var a=Array.prototype.slice.apply(a),d=' ['+b.toUpperCase()+'] [Faye',f=this.className,g=a.shift().replace(/\?/g,function(){try{return Faye.toJSON(a.shift())}catch(e){return'[Object]'}});for(var h in Faye){if(f)continue;if(typeof Faye[h]!=='function')continue;if(this instanceof Faye[h])f=h}if(f)d+='.'+f;d+='] ';Faye.logger(Faye.timestamp()+d+g)}};Faye.each(Faye.Logging.LOG_LEVELS,function(a,b){Faye.Logging[a]=function(){this.log(arguments,a)}});Faye.Grammar={LOWALPHA:/^[a-z]$/,UPALPHA:/^[A-Z]$/,ALPHA:/^([a-z]|[A-Z])$/,DIGIT:/^[0-9]$/,ALPHANUM:/^(([a-z]|[A-Z])|[0-9])$/,MARK:/^(\-|\_|\!|\~|\(|\)|\$|\@)$/,STRING:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*$/,TOKEN:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+$/,INTEGER:/^([0-9])+$/,CHANNEL_SEGMENT:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+$/,CHANNEL_SEGMENTS:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,CHANNEL_NAME:/^\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,WILD_CARD:/^\*{1,2}$/,CHANNEL_PATTERN:/^(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*\/\*{1,2}$/,VERSION_ELEMENT:/^(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*$/,VERSION:/^([0-9])+(\.(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*)*$/,CLIENT_ID:/^((([a-z]|[A-Z])|[0-9]))+$/,ID:/^((([a-z]|[A-Z])|[0-9]))+$/,ERROR_MESSAGE:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*$/,ERROR_ARGS:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*$/,ERROR_CODE:/^[0-9][0-9][0-9]$/,ERROR:/^([0-9][0-9][0-9]:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*|[0-9][0-9][0-9]::(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)$/};Faye.Extensible={addExtension:function(a){this._5=this._5||[];this._5.push(a);if(a.added)a.added()},removeExtension:function(a){if(!this._5)return;var b=this._5.length;while(b--){if(this._5[b]!==a)continue;this._5.splice(b,1);if(a.removed)a.removed()}},pipeThroughExtensions:function(c,d,f,g){this.debug('Passing through ? extensions: ?',c,d);if(!this._5)return f.call(g,d);var h=this._5.slice();var i=function(a){if(!a)return f.call(g,a);var b=h.shift();if(!b)return f.call(g,a);if(b[c])b[c](a,i);else i(a)};i(d)}};Faye.extend(Faye.Extensible,Faye.Logging);Faye.Channel=Faye.Class({initialize:function(a){this.id=this.name=a},push:function(a){this.publishEvent('message',a)},isUnused:function(){return this.countSubscribers('message')===0}});Faye.extend(Faye.Channel.prototype,Faye.Publisher);Faye.extend(Faye.Channel,{HANDSHAKE:'/meta/handshake',CONNECT:'/meta/connect',SUBSCRIBE:'/meta/subscribe',UNSUBSCRIBE:'/meta/unsubscribe',DISCONNECT:'/meta/disconnect',META:'meta',SERVICE:'service',expand:function(a){var b=this.parse(a),c=['/**',a];var d=b.slice();d[d.length-1]='*';c.push(this.unparse(d));for(var f=1,g=b.length;f<g;f++){d=b.slice(0,f);d.push('**');c.push(this.unparse(d))}return c},isValid:function(a){return Faye.Grammar.CHANNEL_NAME.test(a)||Faye.Grammar.CHANNEL_PATTERN.test(a)},parse:function(a){if(!this.isValid(a))return null;return a.split('/').slice(1)},unparse:function(a){return'/'+a.join('/')},isMeta:function(a){var b=this.parse(a);return b?(b[0]===this.META):null},isService:function(a){var b=this.parse(a);return b?(b[0]===this.SERVICE):null},isSubscribable:function(a){if(!this.isValid(a))return null;return!this.isMeta(a)&&!this.isService(a)},Set:Faye.Class({initialize:function(){this._2={}},getKeys:function(){var c=[];Faye.each(this._2,function(a,b){c.push(a)});return c},remove:function(a){delete this._2[a]},hasSubscription:function(a){return this._2.hasOwnProperty(a)},subscribe:function(c,d,f){if(!d)return;Faye.each(c,function(a){var b=this._2[a]=this._2[a]||new Faye.Channel(a);b.addSubscriber('message',d,f)},this)},unsubscribe:function(a,b,c){var d=this._2[a];if(!d)return false;d.removeSubscriber('message',b,c);if(d.isUnused()){this.remove(a);return true}else{return false}},distributeMessage:function(c){var d=Faye.Channel.expand(c.channel);Faye.each(d,function(a){var b=this._2[a];if(b)b.publishEvent('message',c.data)},this)}})});Faye.Subscription=Faye.Class({initialize:function(a,b,c,d){this._8=a;this._2=b;this._m=c;this._n=d;this._u=false},cancel:function(){if(this._u)return;this._8.unsubscribe(this._2,this._m,this._n);this._u=true},unsubscribe:function(){this.cancel()}});Faye.extend(Faye.Subscription.prototype,Faye.Deferrable);Faye.Client=Faye.Class({UNCONNECTED:1,CONNECTING:2,CONNECTED:3,DISCONNECTED:4,HANDSHAKE:'handshake',RETRY:'retry',NONE:'none',CONNECTION_TIMEOUT:60.0,DEFAULT_ENDPOINT:'/bayeux',INTERVAL:0.0,initialize:function(b,c){this.info('New client created for ?',b);this.endpoint=b||this.DEFAULT_ENDPOINT;this._v=c||{};Faye.Transport.get(this,Faye.MANDATORY_CONNECTION_TYPES,function(a){this._d=a},this);this._1=this.UNCONNECTED;this._2=new Faye.Channel.Set();this._e=0;this._o={};this._6={reconnect:this.RETRY,interval:1000*(this._v.interval||this.INTERVAL),timeout:1000*(this._v.timeout||this.CONNECTION_TIMEOUT)};if(Faye.Event)Faye.Event.on(Faye.ENV,'beforeunload',this.disconnect,this)},getClientId:function(){return this._0},getState:function(){switch(this._1){case this.UNCONNECTED:return'UNCONNECTED';case this.CONNECTING:return'CONNECTING';case this.CONNECTED:return'CONNECTED';case this.DISCONNECTED:return'DISCONNECTED'}},handshake:function(c,d){if(this._6.reconnect===this.NONE)return;if(this._1!==this.UNCONNECTED)return;this._1=this.CONNECTING;var f=this;this.info('Initiating handshake with ?',this.endpoint);this._9({channel:Faye.Channel.HANDSHAKE,version:Faye.BAYEUX_VERSION,supportedConnectionTypes:[this._d.connectionType]},function(b){if(b.successful){this._1=this.CONNECTED;this._0=b.clientId;Faye.Transport.get(this,b.supportedConnectionTypes,function(a){this._d=a},this);this.info('Handshake successful: ?',this._0);this.subscribe(this._2.getKeys(),true);if(c)c.call(d)}else{this.info('Handshake unsuccessful');Faye.ENV.setTimeout(function(){f.handshake(c,d)},this._6.interval);this._1=this.UNCONNECTED}},this)},connect:function(a,b){if(this._6.reconnect===this.NONE)return;if(this._1===this.DISCONNECTED)return;if(this._1===this.UNCONNECTED)return this.handshake(function(){this.connect(a,b)},this);this.callback(a,b);if(this._1!==this.CONNECTED)return;this.info('Calling deferred actions for ?',this._0);this.setDeferredStatus('succeeded');this.setDeferredStatus('deferred');if(this._p)return;this._p=true;this.info('Initiating connection for ?',this._0);this._9({channel:Faye.Channel.CONNECT,clientId:this._0,connectionType:this._d.connectionType},this._w,this)},disconnect:function(){if(this._1!==this.CONNECTED)return;this._1=this.DISCONNECTED;this.info('Disconnecting ?',this._0);this._9({channel:Faye.Channel.DISCONNECT,clientId:this._0});this.info('Clearing channel listeners for ?',this._0);this._2=new Faye.Channel.Set()},subscribe:function(c,d,f){if(c instanceof Array)return Faye.each(c,function(channel){this.subscribe(channel,d,f)},this);var g=new Faye.Subscription(this,c,d,f);var h=(d===true);if(!h&&this._2.hasSubscription(c)){this._2.subscribe([c],d,f);g.setDeferredStatus('succeeded');return g}this.connect(function(){this.info('Client ? attempting to subscribe to ?',this._0,c);this._9({channel:Faye.Channel.SUBSCRIBE,clientId:this._0,subscription:c},function(a){if(!a.successful)return g.setDeferredStatus('failed',Faye.Error.parse(a.error));var b=[].concat(a.subscription);this.info('Subscription acknowledged for ? to ?',this._0,b);if(!h)this._2.subscribe(b,d,f);g.setDeferredStatus('succeeded')},this)},this);return g},unsubscribe:function(c,d,f){if(c instanceof Array)return Faye.each(c,function(channel){this.unsubscribe(channel,d,f)},this);var g=this._2.unsubscribe(c,d,f);if(!g)return;this.connect(function(){this.info('Client ? attempting to unsubscribe from ?',this._0,c);this._9({channel:Faye.Channel.UNSUBSCRIBE,clientId:this._0,subscription:c},function(a){if(!a.successful)return;var b=[].concat(a.subscription);this.info('Unsubscription acknowledged for ? from ?',this._0,b)},this)},this)},publish:function(a,b){if(!Faye.Grammar.CHANNEL_NAME.test(a))throw new Error("Cannot publish: '"+a+"' is not a valid channel name");this.connect(function(){this.info('Client ? queueing published message to ?: ?',this._0,a,b);this._9({channel:a,data:b,clientId:this._0})},this)},receiveMessage:function(c){this.pipeThroughExtensions('incoming',c,function(a){if(!a)return;if(a.advice)this._z(a.advice);var b=this._o[a.id];if(b){delete this._o[a.id];b[0].call(b[1],a)}this._A(a)},this)},_9:function(b,c,d){b.id=this._B();if(c)this._o[b.id]=[c,d];this.pipeThroughExtensions('outgoing',b,function(a){if(!a)return;this._d.send(a,this._6.timeout/1000)},this)},_B:function(){this._e+=1;if(this._e>=Math.pow(2,32))this._e=0;return this._e.toString(36)},_z:function(a){Faye.extend(this._6,a);if(this._6.reconnect===this.HANDSHAKE&&this._1!==this.DISCONNECTED){this._1=this.UNCONNECTED;this._0=null;this._w()}},_A:function(a){if(!a.channel||!a.data)return;this.info('Client ? calling listeners for ? with ?',this._0,a.channel,a.data);this._2.distributeMessage(a)},_C:function(){if(!this._p)return;this._p=null;this.info('Closed connection for ?',this._0)},_w:function(){this._C();var a=this;Faye.ENV.setTimeout(function(){a.connect()},this._6.interval)}});Faye.extend(Faye.Client.prototype,Faye.Deferrable);Faye.extend(Faye.Client.prototype,Faye.Logging);Faye.extend(Faye.Client.prototype,Faye.Extensible);Faye.Transport=Faye.extend(Faye.Class({MAX_DELAY:0.0,batching:true,initialize:function(a,b){this.debug('Created new ? transport for ?',this.connectionType,b);this._8=a;this._a=b;this._f=[]},send:function(a,b){this.debug('Client ? sending message to ?: ?',this._8._0,this._a,a);if(!this.batching)return this.request([a],b);this._f.push(a);this._7=b;if(a.channel===Faye.Channel.HANDSHAKE)return this.flush();if(a.channel===Faye.Channel.CONNECT)this._q=a;this.addTimeout('publish',this.MAX_DELAY,this.flush,this)},flush:function(){this.removeTimeout('publish');if(this._f.length>1&&this._q)this._q.advice={timeout:0};this.request(this._f,this._7);this._q=null;this._f=[]},receive:function(a){this.debug('Client ? received from ?: ?',this._8._0,this._a,a);Faye.each(a,this._8.receiveMessage,this._8)},retry:function(a,b){var c=this;return function(){Faye.ENV.setTimeout(function(){c.request(a,2*b)},1000*b)}}}),{get:function(g,h,i,j){var k=g.endpoint;if(h===undefined)h=this.supportedConnectionTypes();Faye.asyncEach(this._r,function(b,c){var d=b[0],f=b[1];if(Faye.indexOf(h,d)<0)return c();f.isUsable(k,function(a){if(a)i.call(j,new f(g,k));else c()})},function(){throw new Error('Could not find a usable connection type for '+k);})},register:function(a,b){this._r.push([a,b]);b.prototype.connectionType=a},_r:[],supportedConnectionTypes:function(){return Faye.map(this._r,function(a){return a[0]})}});Faye.extend(Faye.Transport.prototype,Faye.Logging);Faye.extend(Faye.Transport.prototype,Faye.Timeouts);Faye.Event={_g:[],on:function(a,b,c,d){var f=function(){c.call(d)};if(a.addEventListener)a.addEventListener(b,f,false);else a.attachEvent('on'+b,f);this._g.push({_h:a,_s:b,_m:c,_n:d,_x:f})},detach:function(a,b,c,d){var f=this._g.length,g;while(f--){g=this._g[f];if((a&&a!==g._h)||(b&&b!==g._s)||(c&&c!==g._m)||(d&&d!==g._n))continue;if(g._h.removeEventListener)g._h.removeEventListener(g._s,g._x,false);else g._h.detachEvent('on'+g._s,g._x);this._g.splice(f,1);g=null}}};Faye.Event.on(Faye.ENV,'unload',Faye.Event.detach,Faye.Event);Faye.URI=Faye.extend(Faye.Class({queryString:function(){var c=[],d;Faye.each(this.params,function(a,b){c.push(encodeURIComponent(a)+'='+encodeURIComponent(b))});return c.join('&')},isLocal:function(){var a=Faye.URI.parse(Faye.ENV.location.href);var b=(a.hostname!==this.hostname)||(a.port!==this.port)||(a.protocol!==this.protocol);return!b},toURL:function(){var a=this.queryString();return this.protocol+this.hostname+':'+this.port+this.pathname+(a?'?'+a:'')}}),{parse:function(d,f){if(typeof d!=='string')return d;var g=new this();var h=function(b,c){d=d.replace(c,function(a){if(a)g[b]=a;return''})};h('protocol',/^https?\:\/+/);h('hostname',/^[^\/\:]+/);h('port',/^:[0-9]+/);Faye.extend(g,{protocol:'http://',hostname:Faye.ENV.location.hostname,port:Faye.ENV.location.port},false);if(!g.port)g.port=(g.protocol==='https://')?'443':'80';g.port=g.port.replace(/\D/g,'');var i=d.split('?'),j=i.shift(),k=i.join('?'),m=k?k.split('&'):[],o=m.length,l={};while(o--){i=m[o].split('=');l[decodeURIComponent(i[0]||'')]=decodeURIComponent(i[1]||'')}if(typeof f==='object')Faye.extend(l,f);g.pathname=j;g.params=l;return g}});if(!this.JSON){JSON={}}(function(){function k(a){return a<10?'0'+a:a}if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(a){return this.getUTCFullYear()+'-'+k(this.getUTCMonth()+1)+'-'+k(this.getUTCDate())+'T'+k(this.getUTCHours())+':'+k(this.getUTCMinutes())+':'+k(this.getUTCSeconds())+'Z'};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(a){return this.valueOf()}}var m=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,o=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,l,p,s={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},n;function r(c){o.lastIndex=0;return o.test(c)?'"'+c.replace(o,function(a){var b=s[a];return typeof b==='string'?b:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+c+'"'}function q(a,b){var c,d,f,g,h=l,i,j=b[a];if(j&&typeof j==='object'&&typeof j.toJSON==='function'){j=j.toJSON(a)}if(typeof n==='function'){j=n.call(b,a,j)}switch(typeof j){case'string':return r(j);case'number':return isFinite(j)?String(j):'null';case'boolean':case'null':return String(j);case'object':if(!j){return'null'}l+=p;i=[];if(Object.prototype.toString.apply(j)==='[object Array]'){g=j.length;for(c=0;c<g;c+=1){i[c]=q(c,j)||'null'}f=i.length===0?'[]':l?'[\n'+l+i.join(',\n'+l)+'\n'+h+']':'['+i.join(',')+']';l=h;return f}if(n&&typeof n==='object'){g=n.length;for(c=0;c<g;c+=1){d=n[c];if(typeof d==='string'){f=q(d,j);if(f){i.push(r(d)+(l?': ':':')+f)}}}}else{for(d in j){if(Object.hasOwnProperty.call(j,d)){f=q(d,j);if(f){i.push(r(d)+(l?': ':':')+f)}}}}f=i.length===0?'{}':l?'{\n'+l+i.join(',\n'+l)+'\n'+h+'}':'{'+i.join(',')+'}';l=h;return f}}Faye.stringify=function(a,b,c){var d;l='';p='';if(typeof c==='number'){for(d=0;d<c;d+=1){p+=' '}}else if(typeof c==='string'){p=c}n=b;if(b&&typeof b!=='function'&&(typeof b!=='object'||typeof b.length!=='number')){throw new Error('JSON.stringify');}return q('',{'':a})};if(typeof JSON.stringify!=='function'){JSON.stringify=Faye.stringify}if(typeof JSON.parse!=='function'){JSON.parse=function(g,h){var i;function j(a,b){var c,d,f=a[b];if(f&&typeof f==='object'){for(c in f){if(Object.hasOwnProperty.call(f,c)){d=j(f,c);if(d!==undefined){f[c]=d}else{delete f[c]}}}}return h.call(a,b,f)}m.lastIndex=0;if(m.test(g)){g=g.replace(m,function(a){return'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(g.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){i=eval('('+g+')');return typeof h==='function'?j({'':i},''):i}throw new SyntaxError('JSON.parse');}}}());Faye.Transport.WebSocket=Faye.extend(Faye.Class(Faye.Transport,{UNCONNECTED:1,CONNECTING:2,CONNECTED:3,batching:false,request:function(b,c){this._7=this._7||c;this._i=this._i||{};Faye.each(b,function(a){this._i[a.id]=a},this);this.withSocket(function(a){a.send(Faye.toJSON(b))})},withSocket:function(a,b){this.callback(a,b);this.connect()},connect:function(){this._1=this._1||this.UNCONNECTED;if(this._1!==this.UNCONNECTED)return;this._1=this.CONNECTING;this._b=new WebSocket(Faye.Transport.WebSocket.getSocketUrl(this._a));var d=this;this._b.onopen=function(){delete d._7;d._1=d.CONNECTED;d.setDeferredStatus('succeeded',d._b)};this._b.onmessage=function(b){var c=[].concat(JSON.parse(b.data));Faye.each(c,function(a){delete d._i[a.id]});d.receive(c)};this._b.onclose=function(){var a=(d._1===d.CONNECTED);d.setDeferredStatus('deferred');d._1=d.UNCONNECTED;delete d._b;if(a)return d.resend();Faye.ENV.setTimeout(function(){d.connect()},1000*d._7);d._7=d._7*2}},resend:function(){var c=Faye.map(this._i,function(a,b){return b});this.request(c)}}),{WEBSOCKET_TIMEOUT:1000,getSocketUrl:function(a){return Faye.URI.parse(a).toURL().replace(/^http(s?):/ig,'ws$1:')},isUsable:function(a,b,c){if(!Faye.ENV.WebSocket)return b.call(c,false);var d=false,f=false,g=this.getSocketUrl(a),h=new WebSocket(g);h.onopen=function(){d=true;h.close();b.call(c,true);f=true;h=null};var i=function(){if(!f&&!d)b.call(c,false);f=true};h.onclose=h.onerror=i;Faye.ENV.setTimeout(i,this.WEBSOCKET_TIMEOUT)}});Faye.extend(Faye.Transport.WebSocket.prototype,Faye.Deferrable);Faye.Transport.register('websocket',Faye.Transport.WebSocket);Faye.Transport.XHR=Faye.extend(Faye.Class(Faye.Transport,{request:function(b,c){var d=this.retry(b,c),f=Faye.URI.parse(this._a).pathname,g=this,h=Faye.ENV.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();h.open('POST',f,true);h.setRequestHeader('Content-Type','application/json');h.setRequestHeader('X-Requested-With','XMLHttpRequest');h.onreadystatechange=function(){if(h.readyState!==4)return;var a=h.status;try{if((a>=200&&a<300)||a===304||a===1223)g.receive(JSON.parse(h.responseText));else d()}catch(e){d()}finally{Faye.Event.detach(Faye.ENV,'beforeunload',i);h.onreadystatechange=function(){};h=null}};var i=function(){h.abort()};Faye.Event.on(Faye.ENV,'beforeunload',i);h.send(Faye.toJSON(b))}}),{isUsable:function(a,b,c){b.call(c,Faye.URI.parse(a).isLocal())}});Faye.Transport.register('long-polling',Faye.Transport.XHR);Faye.Transport.CORS=Faye.extend(Faye.Class(Faye.Transport,{request:function(a,b){var c=Faye.ENV.XDomainRequest?XDomainRequest:XMLHttpRequest,d=new c(),f=this.retry(a,b),g=this;d.open('POST',this._a,true);d.onload=function(){try{g.receive(JSON.parse(d.responseText))}catch(e){f()}finally{d.onload=d.onerror=null;d=null}};d.onerror=f;d.onprogress=function(){};d.send('message='+encodeURIComponent(Faye.toJSON(a)))}}),{isUsable:function(a,b,c){if(Faye.URI.parse(a).isLocal())return b.call(c,false);if(Faye.ENV.XDomainRequest)return b.call(c,true);if(Faye.ENV.XMLHttpRequest){var d=new Faye.ENV.XMLHttpRequest();return b.call(c,d.withCredentials!==undefined)}return b.call(c,false)}});Faye.Transport.register('cross-origin-long-polling',Faye.Transport.CORS);Faye.Transport.JSONP=Faye.extend(Faye.Class(Faye.Transport,{request:function(b,c){var d={message:Faye.toJSON(b)},f=document.getElementsByTagName('head')[0],g=document.createElement('script'),h=Faye.Transport.JSONP.getCallbackName(),i=Faye.URI.parse(this._a,d),j=this;var k=function(){if(!g.parentNode)return false;g.parentNode.removeChild(g);return true};Faye.ENV[h]=function(a){Faye.ENV[h]=undefined;try{delete Faye.ENV[h]}catch(e){}if(!k())return;j.receive(a)};Faye.ENV.setTimeout(function(){if(!Faye.ENV[h])return;k();j.request(b,2*c)},1000*c);i.params.jsonp=h;g.type='text/javascript';g.src=i.toURL();f.appendChild(g)}}),{_y:0,getCallbackName:function(){this._y+=1;return'__jsonp'+this._y+'__'},isUsable:function(a,b,c){b.call(c,true)}});Faye.Transport.register('callback-polling',Faye.Transport.JSONP);
|
data/lib/faye.rb
CHANGED
data/lib/faye/engines/base.rb
CHANGED
data/lib/faye/engines/memory.rb
CHANGED
@@ -2,6 +2,8 @@ module Faye
|
|
2
2
|
module Engine
|
3
3
|
|
4
4
|
class Memory < Base
|
5
|
+
include Timeouts
|
6
|
+
|
5
7
|
def initialize(options)
|
6
8
|
@namespace = Namespace.new
|
7
9
|
@clients = {}
|
@@ -36,11 +38,10 @@ module Faye
|
|
36
38
|
end
|
37
39
|
|
38
40
|
def ping(client_id)
|
39
|
-
|
40
|
-
|
41
|
-
debug 'Ping ?, ?', client_id, timeout
|
41
|
+
return unless Numeric === @timeout
|
42
|
+
debug 'Ping ?, ?', client_id, @timeout
|
42
43
|
remove_timeout(client_id)
|
43
|
-
add_timeout(client_id, 2 * timeout) { destroy_client(client_id) }
|
44
|
+
add_timeout(client_id, 2 * @timeout) { destroy_client(client_id) }
|
44
45
|
end
|
45
46
|
|
46
47
|
def subscribe(client_id, channel, &callback)
|
data/lib/faye/engines/redis.rb
CHANGED
@@ -2,17 +2,21 @@ module Faye
|
|
2
2
|
module Engine
|
3
3
|
|
4
4
|
class Redis < Base
|
5
|
-
DEFAULT_HOST
|
6
|
-
DEFAULT_PORT
|
5
|
+
DEFAULT_HOST = 'localhost'
|
6
|
+
DEFAULT_PORT = 6379
|
7
|
+
DEFAULT_DATABASE = 0
|
8
|
+
DEFAULT_GC = 60
|
9
|
+
LOCK_TIMEOUT = 120
|
7
10
|
|
8
11
|
def init
|
9
12
|
return if @redis
|
10
13
|
require 'em-hiredis'
|
11
14
|
|
12
|
-
host = @options[:host]
|
13
|
-
port = @options[:port]
|
14
|
-
db = @options[:database]
|
15
|
+
host = @options[:host] || DEFAULT_HOST
|
16
|
+
port = @options[:port] || DEFAULT_PORT
|
17
|
+
db = @options[:database] || 0
|
15
18
|
auth = @options[:password]
|
19
|
+
gc = @options[:gc] || DEFAULT_GC
|
16
20
|
@ns = @options[:namespace] || ''
|
17
21
|
|
18
22
|
@redis = EventMachine::Hiredis::Client.connect(host, port)
|
@@ -29,16 +33,19 @@ module Faye
|
|
29
33
|
@subscriber.on(:message) do |topic, message|
|
30
34
|
empty_queue(message) if topic == @ns + '/notifications'
|
31
35
|
end
|
36
|
+
|
37
|
+
@gc = EventMachine.add_periodic_timer(gc, &method(:gc))
|
32
38
|
end
|
33
39
|
|
34
40
|
def disconnect
|
35
41
|
@subscriber.unsubscribe(@ns + '/notifications')
|
42
|
+
EventMachine.cancel_timer(@gc)
|
36
43
|
end
|
37
44
|
|
38
45
|
def create_client(&callback)
|
39
46
|
init
|
40
47
|
client_id = Faye.random
|
41
|
-
@redis.
|
48
|
+
@redis.zadd(@ns + '/clients', 0, client_id) do |added|
|
42
49
|
if added == 0
|
43
50
|
create_client(&callback)
|
44
51
|
else
|
@@ -51,12 +58,9 @@ module Faye
|
|
51
58
|
|
52
59
|
def destroy_client(client_id, &callback)
|
53
60
|
init
|
54
|
-
@redis.
|
61
|
+
@redis.zrem(@ns + '/clients', client_id)
|
55
62
|
@redis.del(@ns + "/clients/#{client_id}/messages")
|
56
63
|
|
57
|
-
remove_timeout(client_id)
|
58
|
-
@redis.del(@ns + "/clients/#{client_id}/ping")
|
59
|
-
|
60
64
|
@redis.smembers(@ns + "/clients/#{client_id}/channels") do |channels|
|
61
65
|
n, i = channels.size, 0
|
62
66
|
if n == 0
|
@@ -78,25 +82,18 @@ module Faye
|
|
78
82
|
|
79
83
|
def client_exists(client_id, &callback)
|
80
84
|
init
|
81
|
-
@redis.
|
82
|
-
callback.call(
|
85
|
+
@redis.zscore(@ns + '/clients', client_id) do |score|
|
86
|
+
callback.call(score != nil)
|
83
87
|
end
|
84
88
|
end
|
85
89
|
|
86
90
|
def ping(client_id)
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
return unless Numeric === timeout
|
91
|
+
init
|
92
|
+
return unless Numeric === @timeout
|
91
93
|
|
92
|
-
|
93
|
-
|
94
|
-
@redis.
|
95
|
-
add_timeout(client_id, 2 * timeout) do
|
96
|
-
@redis.get(@ns + "/clients/#{client_id}/ping") do |ping|
|
97
|
-
destroy_client(client_id) if ping == time
|
98
|
-
end
|
99
|
-
end
|
94
|
+
time = Time.now.to_i
|
95
|
+
debug 'Ping ?, ?', client_id, time
|
96
|
+
@redis.zadd(@ns + '/clients', time, client_id)
|
100
97
|
end
|
101
98
|
|
102
99
|
def subscribe(client_id, channel, &callback)
|
@@ -120,15 +117,16 @@ module Faye
|
|
120
117
|
def publish(message)
|
121
118
|
init
|
122
119
|
debug 'Publishing message ?', message
|
120
|
+
|
123
121
|
json_message = JSON.dump(message)
|
124
|
-
channels
|
125
|
-
channels.
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
122
|
+
channels = Channel.expand(message['channel'])
|
123
|
+
keys = channels.map { |c| @ns + "/channels#{c}" }
|
124
|
+
|
125
|
+
@redis.sunion(*keys) do |clients|
|
126
|
+
clients.each do |client_id|
|
127
|
+
debug 'Queueing for client ?: ?', client_id, message
|
128
|
+
@redis.rpush(@ns + "/clients/#{client_id}/messages", json_message)
|
129
|
+
@redis.publish(@ns + '/notifications', client_id)
|
132
130
|
end
|
133
131
|
end
|
134
132
|
end
|
@@ -140,13 +138,58 @@ module Faye
|
|
140
138
|
init
|
141
139
|
|
142
140
|
key = @ns + "/clients/#{client_id}/messages"
|
143
|
-
@redis.
|
141
|
+
@redis.lrange(key, 0, -1) do |json_messages|
|
142
|
+
@redis.ltrim(key, json_messages.size, -1)
|
144
143
|
json_messages.each do |json_message|
|
145
|
-
@redis.srem(key, json_message)
|
146
144
|
conn.deliver(JSON.parse(json_message))
|
147
145
|
end
|
148
146
|
end
|
149
147
|
end
|
148
|
+
|
149
|
+
def gc
|
150
|
+
return unless Numeric === @timeout
|
151
|
+
with_lock 'gc' do |release_lock|
|
152
|
+
cutoff = Time.now.to_i - 2 * @timeout
|
153
|
+
@redis.zrangebyscore(@ns + '/clients', 0, cutoff) do |clients|
|
154
|
+
i, n = 0, clients.size
|
155
|
+
if i == n
|
156
|
+
release_lock.call
|
157
|
+
else
|
158
|
+
clients.each do |client_id|
|
159
|
+
destroy_client(client_id) do
|
160
|
+
i += 1
|
161
|
+
release_lock.call if i == n
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def with_lock(lock_name, &block)
|
170
|
+
lock_key = @ns + '/locks/' + lock_name
|
171
|
+
current_time = Time.now.to_i * 1000
|
172
|
+
expiry = current_time + LOCK_TIMEOUT * 1000 + 1
|
173
|
+
|
174
|
+
release_lock = lambda do
|
175
|
+
@redis.del(lock_key) if Time.now.to_i * 1000 < expiry
|
176
|
+
end
|
177
|
+
|
178
|
+
@redis.setnx(lock_key, expiry) do |set|
|
179
|
+
if set == 1
|
180
|
+
block.call(release_lock)
|
181
|
+
else
|
182
|
+
@redis.get(lock_key) do |timeout|
|
183
|
+
lock_timeout = timeout.to_i(10)
|
184
|
+
if lock_timeout < current_time
|
185
|
+
@redis.getset(lock_key, expiry) do |old_value|
|
186
|
+
block.call(release_lock) if old_value == timeout
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
150
193
|
end
|
151
194
|
|
152
195
|
register 'redis', Redis
|
data/lib/faye/protocol/client.rb
CHANGED
@@ -21,14 +21,14 @@ module Faye
|
|
21
21
|
def initialize(endpoint = nil, options = {})
|
22
22
|
info('New client created for ?', endpoint)
|
23
23
|
|
24
|
-
@endpoint
|
25
|
-
@options
|
26
|
-
|
27
|
-
@transport
|
28
|
-
@state
|
29
|
-
@channels
|
24
|
+
@endpoint = endpoint || RackAdapter::DEFAULT_ENDPOINT
|
25
|
+
@options = options
|
26
|
+
|
27
|
+
@transport = Transport.get(self, MANDATORY_CONNECTION_TYPES)
|
28
|
+
@state = UNCONNECTED
|
29
|
+
@channels = Channel::Set.new
|
30
|
+
@message_id = 0
|
30
31
|
|
31
|
-
@namespace = Namespace.new
|
32
32
|
@response_callbacks = {}
|
33
33
|
|
34
34
|
@advice = {
|
@@ -286,14 +286,20 @@ module Faye
|
|
286
286
|
private
|
287
287
|
|
288
288
|
def send(message, &callback)
|
289
|
-
message['id'] =
|
289
|
+
message['id'] = generate_message_id
|
290
290
|
@response_callbacks[message['id']] = callback if callback
|
291
291
|
|
292
292
|
pipe_through_extensions(:outgoing, message) do |message|
|
293
293
|
@transport.send(message, @advice['timeout'] / 1000.0) if message
|
294
294
|
end
|
295
295
|
end
|
296
|
-
|
296
|
+
|
297
|
+
def generate_message_id
|
298
|
+
@message_id += 1
|
299
|
+
@message_id = 0 if @message_id >= 2**32
|
300
|
+
@message_id.to_s(36)
|
301
|
+
end
|
302
|
+
|
297
303
|
def handle_advice(advice)
|
298
304
|
@advice.update(advice)
|
299
305
|
|
data/lib/faye/protocol/server.rb
CHANGED
@@ -73,16 +73,17 @@ module Faye
|
|
73
73
|
info 'Handling message: ? (local: ?)', message, local
|
74
74
|
|
75
75
|
channel_name = message['channel']
|
76
|
+
|
77
|
+
return handle_meta(message, local, &callback) if Channel.meta?(channel_name)
|
78
|
+
|
76
79
|
@engine.publish(message) unless message['error'] or Grammar::CHANNEL_NAME !~ channel_name
|
77
80
|
|
78
|
-
if
|
79
|
-
handle_meta(message, local, &callback)
|
80
|
-
elsif message['clientId'].nil?
|
81
|
-
callback.call([])
|
82
|
-
else
|
81
|
+
if message['clientId']
|
83
82
|
response = make_response(message)
|
84
83
|
response['successful'] = !response['error']
|
85
84
|
callback.call([response])
|
85
|
+
else
|
86
|
+
callback.call([])
|
86
87
|
end
|
87
88
|
end
|
88
89
|
|
@@ -97,6 +98,8 @@ module Faye
|
|
97
98
|
end
|
98
99
|
|
99
100
|
def advize(response)
|
101
|
+
return unless [Channel::HANDSHAKE, Channel::CONNECT].include?(response['channel'])
|
102
|
+
|
100
103
|
advice = response['advice'] ||= {}
|
101
104
|
if response['error']
|
102
105
|
advice['reconnect'] ||= 'handshake'
|
data/lib/faye/transport/local.rb
CHANGED
data/spec/browser.html
CHANGED
@@ -8,6 +8,14 @@
|
|
8
8
|
<body>
|
9
9
|
<script type="text/javascript">
|
10
10
|
|
11
|
+
if (typeof TestSwarm === 'undefined')
|
12
|
+
TestSwarm = {
|
13
|
+
submit: function(result) {
|
14
|
+
if (window.console) console.log(Faye.toJSON(result));
|
15
|
+
},
|
16
|
+
heartbeat: function() {}
|
17
|
+
}
|
18
|
+
|
11
19
|
JS.Packages(function() { with(this) {
|
12
20
|
file('../build/faye-browser-min.js').provides('Faye')
|
13
21
|
autoload(/.*Spec/, {from: './javascript'})
|
data/spec/install.sh
CHANGED
@@ -1,16 +1,21 @@
|
|
1
1
|
# This script installs all the necessary software to run the Ruby and
|
2
|
-
# Node versions of Faye
|
2
|
+
# Node versions of Faye, as well as the load testing tools AB and Tsung.
|
3
|
+
|
4
|
+
# Tested on Ubuntu 10.04 LTS 64-bit EC2 image:
|
3
5
|
# http://uec-images.ubuntu.com/releases/10.04/release/
|
4
6
|
|
5
|
-
FAYE_BRANCH=
|
6
|
-
NODE_VERSION=0.4.
|
7
|
-
|
7
|
+
FAYE_BRANCH=master
|
8
|
+
NODE_VERSION=0.4.8
|
9
|
+
PHANTOM_VERSION=1.2
|
10
|
+
REDIS_VERSION=2.2.11
|
8
11
|
RUBY_VERSION=1.9.2
|
12
|
+
TSUNG_VERSION=1.3.3
|
9
13
|
|
10
14
|
sudo apt-get update
|
11
|
-
sudo apt-get install build-essential g++ git-core \
|
15
|
+
sudo apt-get install build-essential g++ git-core curl wget \
|
12
16
|
openssl libcurl4-openssl-dev libreadline-dev \
|
13
|
-
|
17
|
+
apache2-utils erlang gnuplot \
|
18
|
+
libqt4-dev qt4-qmake xvfb
|
14
19
|
|
15
20
|
bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)
|
16
21
|
echo "source \"\$HOME/.rvm/scripts/rvm\"" | tee -a ~/.bashrc
|
@@ -22,7 +27,7 @@ echo "install: --no-rdoc --no-ri
|
|
22
27
|
update: --no-rdoc --no-ri" | tee ~/.gemrc
|
23
28
|
gem install rake bundler
|
24
29
|
|
25
|
-
cd
|
30
|
+
cd ~
|
26
31
|
git clone git://github.com/creationix/nvm.git ~/.nvm
|
27
32
|
. ~/.nvm/nvm.sh
|
28
33
|
echo ". ~/.nvm/nvm.sh" | tee -a ~/.bashrc
|
@@ -38,7 +43,27 @@ sudo make
|
|
38
43
|
sudo ln -s /usr/src/redis-$REDIS_VERSION/src/redis-server /usr/bin/redis-server
|
39
44
|
sudo ln -s /usr/src/redis-$REDIS_VERSION/src/redis-cli /usr/bin/redis-cli
|
40
45
|
|
41
|
-
cd
|
46
|
+
cd /usr/src
|
47
|
+
sudo git clone git://github.com/ariya/phantomjs.git
|
48
|
+
cd phantomjs
|
49
|
+
sudo git checkout $PHANTOM_VERSION
|
50
|
+
sudo qmake-qt4
|
51
|
+
sudo make
|
52
|
+
sudo ln -s /usr/src/phantomjs/bin/phantomjs /usr/bin/phantomjs
|
53
|
+
echo "To use phantomjs, run DISPLAY=:1 Xvfb :1 -screen 0 1024x768x16"
|
54
|
+
|
55
|
+
cd /usr/src
|
56
|
+
sudo wget http://tsung.erlang-projects.org/dist/tsung-$TSUNG_VERSION.tar.gz
|
57
|
+
sudo tar zxvf tsung-$TSUNG_VERSION.tar.gz
|
58
|
+
cd tsung-$TSUNG_VERSION
|
59
|
+
sudo ./configure
|
60
|
+
sudo make
|
61
|
+
sudo make install
|
62
|
+
sudo ln -s /usr/lib/tsung/bin/tsung_stats.pl /usr/bin/tsung-stats
|
63
|
+
echo "To use tsung-stats you need to 'install Template' from CPAN"
|
64
|
+
sudo perl -MCPAN -eshell
|
65
|
+
|
66
|
+
cd ~
|
42
67
|
git clone git://github.com/jcoglan/faye.git
|
43
68
|
cd faye
|
44
69
|
git checkout $FAYE_BRANCH
|
@@ -158,11 +158,12 @@ JS.ENV.ClientSpec = JS.Test.describe("Client", function() { with(this) {
|
|
158
158
|
|
159
159
|
client.receiveMessage({advice: {reconnect: "handshake"}})
|
160
160
|
|
161
|
-
stubResponse({channel:
|
162
|
-
successful:
|
163
|
-
version:
|
161
|
+
stubResponse({channel: "/meta/handshake",
|
162
|
+
successful: true,
|
163
|
+
version: "1.0",
|
164
164
|
supportedConnectionTypes: ["websocket"],
|
165
|
-
clientId:
|
165
|
+
clientId: "reconnectid",
|
166
|
+
subscription: "/messages/foo" }) // tacked on to trigger subscribe() callback
|
166
167
|
}})
|
167
168
|
|
168
169
|
it("resends the subscriptions to the server", function() { with(this) {
|
@@ -135,7 +135,7 @@ JS.ENV.EngineSpec = JS.Test.describe("Pub/sub engines", function() { with(this)
|
|
135
135
|
}})
|
136
136
|
|
137
137
|
describe("ping", function() { with(this) {
|
138
|
-
define("options", function() { return {timeout: 0.3} })
|
138
|
+
define("options", function() { return {timeout: 0.3, gc: 0.08} })
|
139
139
|
|
140
140
|
it("removes a client if it does not ping often enough", function() { with(this) {
|
141
141
|
clock_tick(700)
|
@@ -27,7 +27,7 @@ JS.ENV.Server.ExtensionsSpec = JS.Test.describe("Server extensions", function()
|
|
27
27
|
stub(engine, "publish")
|
28
28
|
var response = null
|
29
29
|
server.process({channel: "/meta/handshake"}, false, function(r) { response = r })
|
30
|
-
assertEqual( [{channel: "/foo", data: "hello"
|
30
|
+
assertEqual( [{channel: "/foo", data: "hello"}], response )
|
31
31
|
}})
|
32
32
|
}})
|
33
33
|
|
@@ -53,7 +53,7 @@ JS.ENV.Server.ExtensionsSpec = JS.Test.describe("Server extensions", function()
|
|
53
53
|
stub(engine, "publish")
|
54
54
|
var response = null
|
55
55
|
server.process({channel: "/meta/handshake"}, false, function(r) { response = r })
|
56
|
-
assertEqual( [{channel: "/foo", data: "hello",
|
56
|
+
assertEqual( [{channel: "/foo", data: "hello", ext: {auth: "password"}}], response )
|
57
57
|
}})
|
58
58
|
}})
|
59
59
|
}})
|
@@ -32,7 +32,6 @@ JS.ENV.ServerSpec = JS.Test.describe("Server", function() { with(this) {
|
|
32
32
|
|
33
33
|
it("routes single messages to appropriate handlers", function() { with(this) {
|
34
34
|
expect(server, "handshake").given(handshake, false).yielding([{}])
|
35
|
-
expect(engine, "publish").given(handshake)
|
36
35
|
server.process(handshake, false, function() {})
|
37
36
|
}})
|
38
37
|
|
@@ -43,11 +42,12 @@ JS.ENV.ServerSpec = JS.Test.describe("Server", function() { with(this) {
|
|
43
42
|
expect(server, "subscribe").given(subscribe, false).yielding([{}])
|
44
43
|
expect(server, "unsubscribe").given(unsubscribe, false).yielding([{}])
|
45
44
|
|
46
|
-
expect(engine, "publish").given(handshake)
|
47
|
-
expect(engine, "publish").given(connect)
|
48
|
-
expect(engine, "publish").given(disconnect)
|
49
|
-
expect(engine, "publish").given(subscribe)
|
50
|
-
expect(engine, "publish").given(unsubscribe)
|
45
|
+
expect(engine, "publish").given(handshake).exactly(0)
|
46
|
+
expect(engine, "publish").given(connect).exactly(0)
|
47
|
+
expect(engine, "publish").given(disconnect).exactly(0)
|
48
|
+
expect(engine, "publish").given(subscribe).exactly(0)
|
49
|
+
expect(engine, "publish").given(unsubscribe).exactly(0)
|
50
|
+
|
51
51
|
expect(engine, "publish").given(publish)
|
52
52
|
|
53
53
|
server.process([handshake, connect, disconnect, subscribe, unsubscribe, publish], false, function() {})
|
@@ -59,7 +59,7 @@ JS.ENV.ServerSpec = JS.Test.describe("Server", function() { with(this) {
|
|
59
59
|
server.process(publish, false, function() {})
|
60
60
|
}})
|
61
61
|
|
62
|
-
it("returns no
|
62
|
+
it("returns no response", function() { with(this) {
|
63
63
|
stub(engine, "publish")
|
64
64
|
server.process(publish, false, function(response) {
|
65
65
|
assertEqual( [], response)
|
@@ -98,14 +98,14 @@ JS.ENV.ServerSpec = JS.Test.describe("Server", function() { with(this) {
|
|
98
98
|
|
99
99
|
describe("handshaking", function() { with(this) {
|
100
100
|
before(function() { with(this) {
|
101
|
-
expect(
|
102
|
-
expect(server, "handshake").given(handshake, false).yielding([{successful: true}])
|
101
|
+
expect(server, "handshake").given(handshake, false).yielding([{channel: "/meta/handshake", successful: true}])
|
103
102
|
}})
|
104
103
|
|
105
104
|
it("returns the handshake response with advice", function() { with(this) {
|
106
105
|
server.process(handshake, false, function(response) {
|
107
106
|
assertEqual([
|
108
|
-
{
|
107
|
+
{ channel: "/meta/handshake",
|
108
|
+
successful: true,
|
109
109
|
advice: {reconnect: "retry", interval: 0, timeout: 60000}
|
110
110
|
}
|
111
111
|
], response)
|
@@ -116,7 +116,6 @@ JS.ENV.ServerSpec = JS.Test.describe("Server", function() { with(this) {
|
|
116
116
|
describe("connecting for messages", function() { with(this) {
|
117
117
|
before(function() { with(this) {
|
118
118
|
this.messages = [{channel: "/a"}, {channel: "/b"}]
|
119
|
-
expect(engine, "publish").given(connect)
|
120
119
|
expect(server, "connect").given(connect, false).yielding([messages])
|
121
120
|
}})
|
122
121
|
|
data/spec/phantom.js
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
// This script should be run with PhantomJS
|
2
|
+
// http://www.phantomjs.org/
|
3
|
+
|
4
|
+
var page = new WebPage()
|
5
|
+
|
6
|
+
page.onConsoleMessage = function(message) {
|
7
|
+
try {
|
8
|
+
var result = JSON.parse(message)
|
9
|
+
if ('total' in result && 'fail' in result) {
|
10
|
+
console.log(message)
|
11
|
+
var status = (!result.fail && !result.error) ? 0 : 1
|
12
|
+
phantom.exit(status)
|
13
|
+
}
|
14
|
+
} catch (e) {}
|
15
|
+
}
|
16
|
+
|
17
|
+
page.open('spec/browser.html')
|
@@ -10,7 +10,6 @@ describe "server extensions" do
|
|
10
10
|
|
11
11
|
let(:server) { Faye::Server.new }
|
12
12
|
let(:message) { {"channel" => "/foo", "data" => "hello"} }
|
13
|
-
let(:advice) { {"reconnect"=>"retry", "interval"=>0, "timeout"=>60000} }
|
14
13
|
|
15
14
|
before do
|
16
15
|
Faye::Engine.stub(:get).and_return engine
|
@@ -37,7 +36,7 @@ describe "server extensions" do
|
|
37
36
|
engine.stub(:publish)
|
38
37
|
response = nil
|
39
38
|
server.process({"channel" => "/meta/handshake"}, false) { |r| response = r }
|
40
|
-
response.should == [{"channel" => "/foo", "data" => "hello"
|
39
|
+
response.should == [{"channel" => "/foo", "data" => "hello"}]
|
41
40
|
end
|
42
41
|
end
|
43
42
|
|
@@ -62,7 +61,7 @@ describe "server extensions" do
|
|
62
61
|
engine.stub(:publish)
|
63
62
|
response = nil
|
64
63
|
server.process({"channel" => "/meta/handshake"}, false) { |r| response = r }
|
65
|
-
response.should == [{"channel" => "/foo", "data" => "hello", "
|
64
|
+
response.should == [{"channel" => "/foo", "data" => "hello", "ext" => {"auth" => "password"}}]
|
66
65
|
end
|
67
66
|
end
|
68
67
|
end
|
data/spec/ruby/server_spec.rb
CHANGED
@@ -35,7 +35,6 @@ describe Faye::Server do
|
|
35
35
|
|
36
36
|
it "routes single messages to appropriate handlers" do
|
37
37
|
server.should_receive(:handshake).with(handshake, false)
|
38
|
-
engine.should_receive(:publish).with(handshake)
|
39
38
|
server.process(handshake, false)
|
40
39
|
end
|
41
40
|
|
@@ -46,11 +45,11 @@ describe Faye::Server do
|
|
46
45
|
server.should_receive(:subscribe).with(subscribe, false)
|
47
46
|
server.should_receive(:unsubscribe).with(unsubscribe, false)
|
48
47
|
|
49
|
-
engine.
|
50
|
-
engine.
|
51
|
-
engine.
|
52
|
-
engine.
|
53
|
-
engine.
|
48
|
+
engine.should_not_receive(:publish).with(handshake)
|
49
|
+
engine.should_not_receive(:publish).with(connect)
|
50
|
+
engine.should_not_receive(:publish).with(disconnect)
|
51
|
+
engine.should_not_receive(:publish).with(subscribe)
|
52
|
+
engine.should_not_receive(:publish).with(unsubscribe)
|
54
53
|
engine.should_receive(:publish).with(publish)
|
55
54
|
|
56
55
|
server.process([handshake, connect, disconnect, subscribe, unsubscribe, publish], false)
|
@@ -93,14 +92,15 @@ describe Faye::Server do
|
|
93
92
|
|
94
93
|
describe "handshaking" do
|
95
94
|
before do
|
96
|
-
|
97
|
-
server.should_receive(:handshake).with(handshake, false).and_yield(
|
95
|
+
response = {"channel" => "/meta/handshake", "successful" => true}
|
96
|
+
server.should_receive(:handshake).with(handshake, false).and_yield(response)
|
98
97
|
end
|
99
98
|
|
100
99
|
it "returns the handshake response with advice" do
|
101
100
|
server.process(handshake, false) do |response|
|
102
101
|
response.should == [
|
103
|
-
{ "
|
102
|
+
{ "channel" => "/meta/handshake",
|
103
|
+
"successful" => true,
|
104
104
|
"advice" => {"reconnect" => "retry", "interval" => 0, "timeout" => 60000}
|
105
105
|
}
|
106
106
|
]
|
@@ -112,7 +112,6 @@ describe Faye::Server do
|
|
112
112
|
let(:messages) { [{"channel" => "/a"}, {"channel" => "/b"}] }
|
113
113
|
|
114
114
|
before do
|
115
|
-
engine.should_receive(:publish).with(connect)
|
116
115
|
server.should_receive(:connect).with(connect, false).and_yield(messages)
|
117
116
|
end
|
118
117
|
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: faye
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.6.
|
5
|
+
version: 0.6.3
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- James Coglan
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-07-10 00:00:00 +01:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -134,6 +134,39 @@ dependencies:
|
|
134
134
|
version: "0"
|
135
135
|
type: :development
|
136
136
|
version_requirements: *id011
|
137
|
+
- !ruby/object:Gem::Dependency
|
138
|
+
name: RedCloth
|
139
|
+
prerelease: false
|
140
|
+
requirement: &id012 !ruby/object:Gem::Requirement
|
141
|
+
none: false
|
142
|
+
requirements:
|
143
|
+
- - ~>
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 3.0.0
|
146
|
+
type: :development
|
147
|
+
version_requirements: *id012
|
148
|
+
- !ruby/object:Gem::Dependency
|
149
|
+
name: sinatra
|
150
|
+
prerelease: false
|
151
|
+
requirement: &id013 !ruby/object:Gem::Requirement
|
152
|
+
none: false
|
153
|
+
requirements:
|
154
|
+
- - ">="
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
version: "0"
|
157
|
+
type: :development
|
158
|
+
version_requirements: *id013
|
159
|
+
- !ruby/object:Gem::Dependency
|
160
|
+
name: staticmatic
|
161
|
+
prerelease: false
|
162
|
+
requirement: &id014 !ruby/object:Gem::Requirement
|
163
|
+
none: false
|
164
|
+
requirements:
|
165
|
+
- - ">="
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: "0"
|
168
|
+
type: :development
|
169
|
+
version_requirements: *id014
|
137
170
|
description:
|
138
171
|
email: jcoglan@gmail.com
|
139
172
|
executables: []
|
@@ -147,6 +180,7 @@ files:
|
|
147
180
|
- README.rdoc
|
148
181
|
- lib/faye-browser-min.js
|
149
182
|
- spec/browser.html
|
183
|
+
- spec/phantom.js
|
150
184
|
- spec/redis.conf
|
151
185
|
- spec/testswarm.pl
|
152
186
|
- spec/install.sh
|