message_bus 2.0.0.beta.5 → 2.0.0.beta.6

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of message_bus might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b91a7d621707536a33738a736ffd00bba4639d46
4
- data.tar.gz: b5a445c066617c126b4bfaeb6444ad0f473ec670
3
+ metadata.gz: 686569cf588c00aa211a970eab14d37cf979c844
4
+ data.tar.gz: 167ad799942ea8f8de1e945015bae4f687c4629f
5
5
  SHA512:
6
- metadata.gz: adb5ec754024c6a62873a98dd2e3978d669f057bda86ffac0a6842787a9b8e14d0280315d6f499f40da9e86ed0a412c8ce0675262318f9330d023e2e58afe4c3
7
- data.tar.gz: 50c1ee4d6b4e485198011c08a5632014321f34ceb388dd93568513d2762d1d9c539a0a3c7b4d2e6e74d7485e4ce5d3ace9d7f0e9bf8be6812df5f4d72f27c10a
6
+ metadata.gz: 6810d52979d00f065b8de475f8cbeb81edbe7ae8fc706d1a818d32ac2f1c36f2f9ee94c3585968beba594dde53064f82a58348c0948a2b0d64326e356880cb32
7
+ data.tar.gz: 0bd9ac5196a77f53797e834b3e296bdad9328fcc6a28331b5e53456527eb5be8df25de76fd3aa05e960f4927531d19f7bfb8ffafdb163006bb1264273d0987d1
data/CHANGELOG CHANGED
@@ -1,5 +1,13 @@
1
1
  29-02-2016
2
2
 
3
+ - Version 2.0.0.beta.6
4
+
5
+ - Feature: Support standalone opertion without depending on jQuery @nathanstitt
6
+ - Feature: Support a noconflict mode @nathanstitt
7
+ - Feature: Support JSON POST payload @nathanstitt
8
+
9
+ 29-02-2016
10
+
3
11
  - Version 2.0.0.beta.5
4
12
 
5
13
  - Fix: JavaScript unsubscribe was not updating publicly visible MessageBus.callbacks @sam
data/README.md CHANGED
@@ -20,7 +20,7 @@ Live chat demo per [examples/chat](https://github.com/SamSaffron/message_bus/tre
20
20
 
21
21
  ## Is this used in production at scale?
22
22
 
23
- **Yes**, MessageBus was extracted out of [Discourse](http://www.discourse.org/) and is used in thousands of production Discourse sites at scale.
23
+ **Yes**, MessageBus was extracted out of [Discourse](http://www.discourse.org/) and is used in thousands of production Discourse sites at scale.
24
24
 
25
25
  ## Installation
26
26
 
@@ -174,7 +174,7 @@ backgroundCallbackInterval|60000|Interval to poll when long polling is disabled
174
174
  maxPollInterval|180000|If request to the server start failing, MessageBus will backoff, this is the upper limit of the backoff.
175
175
  alwaysLongPoll|false|For debugging you may want to disable the "is browser in background" check and always long-poll
176
176
  baseUrl|/|If message bus is mounted in a subdirectory of different domain, you may configure it to perform requests there
177
- ajax|$.ajax|The only dependency on jQuery, you may set up a custom ajax function here
177
+ ajax|$.ajax or XMLHttpRequest|MessageBus will first attempt to use jQuery and then fallback to a plain XMLHttpRequest version that's contained in the `messsage-bus-ajax.js` file. `messsage-bus-ajax.js` must be loaded after `messsage-bus.js` for it to be used.
178
178
 
179
179
  **API**:
180
180
 
@@ -192,6 +192,8 @@ ajax|$.ajax|The only dependency on jQuery, you may set up a custom ajax function
192
192
 
193
193
  `MessageBus.unsubscribe(channel,func)` : Unsubscribe callback from a particular channel
194
194
 
195
+ `MessageBus.noConflict()` : Removes MessageBus from the global namespace by replacing it with whatever was present before MessageBus was loaded. Returns a reference to the MessageBus object.
196
+
195
197
  ## Running tests
196
198
 
197
199
  To run tests you need both Postgres and Redis installed. By default we will connect to the database `message_bus_test` with the current username. If you wish to override this:
@@ -288,7 +290,7 @@ after_fork do |server, worker|
288
290
  end
289
291
  ```
290
292
 
291
- ###
293
+ ###
292
294
 
293
295
  ## Want to help?
294
296
 
@@ -299,5 +301,3 @@ If you are looking to contribute to this project here are some ideas
299
301
  - Improve and properly document admin dashboard (add opt-in stats, better diagnostics into queues)
300
302
  - Improve general documentation (Add examples, refine existing examples)
301
303
  - Make MessageBus a nice website
302
-
303
-
data/Rakefile CHANGED
@@ -3,6 +3,10 @@ require 'rake/testtask'
3
3
  require 'bundler'
4
4
  require 'bundler/gem_tasks'
5
5
  require 'bundler/setup'
6
+ require 'jasmine'
7
+
8
+ ENV['JASMINE_CONFIG_PATH'] ||= File.join(Dir.pwd, 'spec', 'assets', 'support', 'jasmine.yml')
9
+ load 'jasmine/tasks/jasmine.rake'
6
10
 
7
11
  Bundler.require(:default, :test)
8
12
 
@@ -17,7 +21,9 @@ run_spec = proc do |backend|
17
21
  end
18
22
  end
19
23
 
20
- task :spec => [:spec_redis, :spec_postgres, :spec_memory]
24
+ task :spec => [:spec_redis, :spec_postgres, :spec_memory, :spec_client_js]
25
+
26
+ task :spec_client_js => 'jasmine:ci'
21
27
 
22
28
  task :spec_redis do
23
29
  run_spec.call('redis')
@@ -0,0 +1,44 @@
1
+ // A bare-bones implementation of $.ajax that MessageBus will use
2
+ // as a fallback if jQuery is not present
3
+ //
4
+ // Only implements methods & options used by MessageBus
5
+ (function(global, undefined) {
6
+ 'use strict';
7
+ if (!global.MessageBus){
8
+ throw new Error("MessageBus must be loaded before the ajax adapter");
9
+ }
10
+
11
+ var cacheBuster = Math.random() * 10000 | 0;
12
+
13
+ global.MessageBus.ajax = function(options){
14
+ var XHRImpl = (global.MessageBus && global.MessageBus.xhrImplementation) || global.XMLHttpRequest;
15
+ var xhr = new XHRImpl();
16
+ xhr.dataType = options.dataType;
17
+ var url = options.url;
18
+ if (!options.cache){
19
+ url += ((-1 == url.indexOf('?')) ? '?' : '&') + '_=' + (cacheBuster++)
20
+ }
21
+ xhr.open('POST', url);
22
+ for (var name in options.headers){
23
+ xhr.setRequestHeader(name, options.headers[name]);
24
+ }
25
+ xhr.setRequestHeader('Content-Type', 'application/json');
26
+ if (options.messageBus.chunked){
27
+ options.messageBus.onProgressListener(xhr);
28
+ }
29
+ xhr.onreadystatechange = function(){
30
+ if (xhr.readyState === 4){
31
+ var status = xhr.status;
32
+ if (status >= 200 && status < 300 || status === 304){
33
+ options.success(xhr.responseText);
34
+ } else {
35
+ options.error(xhr, xhr.statusText);
36
+ }
37
+ options.complete();
38
+ }
39
+ }
40
+ xhr.send(JSON.stringify(options.data));
41
+ return xhr;
42
+ };
43
+
44
+ })(window);
@@ -1,10 +1,11 @@
1
1
  /*jshint bitwise: false*/
2
- "use strict;"
2
+ (function(global, document, undefined) {
3
+ 'use strict';
4
+ var previousMessageBus = global.MessageBus;
3
5
 
4
- window.MessageBus = (function() {
5
6
  // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
6
7
  var callbacks, clientId, failCount, shouldLongPoll, queue, responseCallbacks, uniqueId, baseUrl;
7
- var me, started, stopped, longPoller, pollTimeout, paused, later;
8
+ var me, started, stopped, longPoller, pollTimeout, paused, later, jQuery, interval, chunkedBackoff;
8
9
 
9
10
  uniqueId = function() {
10
11
  return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
@@ -25,10 +26,9 @@ window.MessageBus = (function() {
25
26
  paused = false;
26
27
  later = [];
27
28
  chunkedBackoff = 0;
28
-
29
+ jQuery = global.jQuery;
29
30
  var hiddenProperty;
30
31
 
31
-
32
32
  (function(){
33
33
  var prefixes = ["","webkit","ms","moz"];
34
34
  for(var i=0; i<prefixes.length; i++) {
@@ -161,38 +161,55 @@ window.MessageBus = (function() {
161
161
  }
162
162
  };
163
163
 
164
+ var setOnProgressListener = function(xhr) {
165
+ var position = 0;
166
+ // if it takes longer than 3000 ms to get first chunk, we have some proxy
167
+ // this is messing with us, so just backoff from using chunked for now
168
+ var chunkedTimeout = setTimeout(disableChunked,3000);
169
+ xhr.onprogress = function () {
170
+ clearTimeout(chunkedTimeout);
171
+ if(xhr.getResponseHeader('Content-Type') === 'application/json; charset=utf-8') {
172
+ // not chunked we are sending json back
173
+ chunked = false;
174
+ return;
175
+ }
176
+ position = handle_progress(xhr.responseText, position);
177
+ }
178
+ };
179
+ if (!me.ajax){
180
+ throw new Error("Either jQuery or the ajax adapter must be loaded");
181
+ }
164
182
  var req = me.ajax({
165
- url: me.baseUrl + "message-bus/" + me.clientId + "/poll?" + (!longPoll ? "dlp=t" : ""),
183
+ url: me.baseUrl + "message-bus/" + me.clientId + "/poll" + (!longPoll ? "?dlp=t" : ""),
166
184
  data: data,
167
185
  cache: false,
168
186
  dataType: dataType,
169
187
  type: 'POST',
170
188
  headers: headers,
189
+ messageBus: {
190
+ chunked: chunked,
191
+ onProgressListener: function(xhr) {
192
+ var position = 0;
193
+ // if it takes longer than 3000 ms to get first chunk, we have some proxy
194
+ // this is messing with us, so just backoff from using chunked for now
195
+ var chunkedTimeout = setTimeout(disableChunked,3000);
196
+ return xhr.onprogress = function () {
197
+ clearTimeout(chunkedTimeout);
198
+ if(xhr.getResponseHeader('Content-Type') === 'application/json; charset=utf-8') {
199
+ chunked = false; // not chunked, we are sending json back
200
+ } else {
201
+ position = handle_progress(xhr.responseText, position);
202
+ }
203
+ }
204
+ }
205
+ },
171
206
  xhr: function() {
172
- var xhr = jQuery.ajaxSettings.xhr();
173
-
174
- if (!chunked) {
175
- return xhr;
176
- }
177
-
178
- var position = 0;
179
-
180
- // if it takes longer than 3000 ms to get first chunk, we have some proxy
181
- // this is messing with us, so just backoff from using chunked for now
182
- var chunkedTimeout = setTimeout(disableChunked,3000);
183
-
184
- xhr.onprogress = function () {
185
- clearTimeout(chunkedTimeout);
186
-
187
- if(xhr.getResponseHeader('Content-Type') === 'application/json; charset=utf-8') {
188
- // not chunked we are sending json back
189
- chunked = false;
190
- return;
191
- }
192
- position = handle_progress(xhr.responseText, position);
193
- }
194
-
195
- return xhr;
207
+ var xhr = jQuery.ajaxSettings.xhr();
208
+ if (!chunked) {
209
+ return xhr;
210
+ }
211
+ this.messageBus.onProgressListener(xhr);
212
+ return xhr;
196
213
  },
197
214
  success: function(messages) {
198
215
  if (!chunked) {
@@ -257,10 +274,11 @@ window.MessageBus = (function() {
257
274
  clientId: clientId,
258
275
  alwaysLongPoll: false,
259
276
  baseUrl: baseUrl,
260
- // TODO we can make the dependency on $ and jQuery conditional
261
- // all we really need is an implementation of ajax
262
- ajax: ($ && $.ajax),
263
-
277
+ ajax: (jQuery && jQuery.ajax),
278
+ noConflict: function(){
279
+ global.MessageBus = global.MessageBus.previousMessageBus;
280
+ return this;
281
+ },
264
282
  diagnostics: function(){
265
283
  console.log("Stopped: " + stopped + " Started: " + started);
266
284
  console.log("Current callbacks");
@@ -317,7 +335,7 @@ window.MessageBus = (function() {
317
335
 
318
336
  // monitor visibility, issue a new long poll when the page shows
319
337
  if(document.addEventListener && 'hidden' in document){
320
- me.visibilityEvent = document.addEventListener('visibilitychange', function(){
338
+ me.visibilityEvent = global.document.addEventListener('visibilitychange', function(){
321
339
  if(!document.hidden && !me.longPoll && pollTimeout){
322
340
  clearTimeout(pollTimeout);
323
341
  pollTimeout = null;
@@ -366,7 +384,7 @@ window.MessageBus = (function() {
366
384
 
367
385
  for (var i=callbacks.length-1; i>=0; i--) {
368
386
 
369
- callback = callbacks[i];
387
+ var callback = callbacks[i];
370
388
  var keep;
371
389
 
372
390
  if (glob) {
@@ -392,6 +410,5 @@ window.MessageBus = (function() {
392
410
  return removed;
393
411
  }
394
412
  };
395
-
396
- return me;
397
- })();
413
+ global.MessageBus = me;
414
+ })(window, document);
@@ -1,3 +1,5 @@
1
+ require 'json'
2
+
1
3
  # our little message bus, accepts long polling and polling
2
4
  module MessageBus::Rack; end
3
5
 
@@ -93,7 +95,8 @@ class MessageBus::Rack::Middleware
93
95
  end
94
96
  else
95
97
  request = Rack::Request.new(env)
96
- request.POST.each do |k,v|
98
+ data = request.content_type.include?('application/json') ? JSON.parse(request.body.read) : request.POST
99
+ data.each do |k,v|
97
100
  if k == "__seq".freeze
98
101
  client.seq = v.to_i
99
102
  else
@@ -203,4 +206,3 @@ class MessageBus::Rack::Middleware
203
206
  }
204
207
  end
205
208
  end
206
-
@@ -1,3 +1,3 @@
1
1
  module MessageBus
2
- VERSION = "2.0.0.beta.5"
2
+ VERSION = "2.0.0.beta.6"
3
3
  end
data/message_bus.gemspec CHANGED
@@ -19,4 +19,5 @@ Gem::Specification.new do |gem|
19
19
  gem.add_runtime_dependency 'rack', '>= 1.1.3'
20
20
  gem.add_development_dependency 'redis'
21
21
  gem.add_development_dependency 'pg'
22
+ gem.add_development_dependency 'jasmine'
22
23
  end
@@ -0,0 +1,93 @@
1
+ var message_id = 1;
2
+ var SEPARATOR = "\r\n|\r\n";
3
+
4
+ var encodeChunks = function(xhr, chunks) {
5
+ if (!chunks || !chunks.length){
6
+ return '';
7
+ }
8
+ for (var i=0;i<chunks.length;i++) {
9
+ var chunk = chunks[i];
10
+ chunk.global_id = Math.random() * 10000 | 0;
11
+ chunk.message_id = message_id++;
12
+ }
13
+ if (xhr.onprogress){ // using longPoll if onprogress is set
14
+ var responses = []
15
+ for (var i=0;i<chunks.length;i++) {
16
+ responses.push( JSON.stringify([chunk]) );
17
+ }
18
+ return responses.join(SEPARATOR) + SEPARATOR;
19
+ } else {
20
+ return chunks;
21
+ }
22
+ }
23
+
24
+ beforeEach(function () {
25
+ var spec = this;
26
+
27
+ function MockedXMLHttpRequest(options){ this.options = options; }
28
+ MockedXMLHttpRequest.prototype.send = function(){
29
+ this.readyState = 4
30
+ this.responseText = encodeChunks(this, spec.responseChunks);
31
+ this.statusText = 'OK';
32
+ this.status = 200;
33
+ if (this.onprogress){ this.onprogress(); }
34
+ this.onreadystatechange()
35
+ }
36
+ MockedXMLHttpRequest.prototype.open = function(){ }
37
+ MockedXMLHttpRequest.prototype.abort = function(){ }
38
+ MockedXMLHttpRequest.prototype.setRequestHeader = function(){ }
39
+ MockedXMLHttpRequest.prototype.getResponseHeader = function(){
40
+ return 'text/plain; charset=utf-8';
41
+ }
42
+ MessageBus.xhrImplementation = MockedXMLHttpRequest
43
+ this.MockedXMLHttpRequest = MockedXMLHttpRequest
44
+ MessageBus.start()
45
+ this.responseChunks = [
46
+ {channel: '/test', data: {password: 'MessageBusRocks!'}}
47
+ ];
48
+
49
+ });
50
+
51
+ window.testMB = function(description, testFn, path, data){
52
+ this.responseChunks = [
53
+ {channel: path || '/test', data: data || {password: 'MessageBusRocks!'}}
54
+ ];
55
+ it(description, function(done){
56
+ spec = this;
57
+ promisy = {
58
+ finally: function(fn){
59
+ this.resolve = fn;
60
+ }
61
+ }
62
+ this.perform = function(specFn){
63
+ var xhrRequest = null;
64
+ spyOn(this.MockedXMLHttpRequest.prototype, 'open').and.callFake(function(method, url){
65
+ xhrRequest = this;
66
+ xhrRequest.url = url
67
+ xhrRequest.method = method
68
+ spec.MockedXMLHttpRequest.prototype.open.and.callThrough(this, method, url);
69
+ })
70
+ MessageBus.subscribe(path || '/test', function(message){
71
+ try {
72
+ specFn.call(spec, message, xhrRequest);
73
+ } catch( error ){
74
+ promisy.resolve.call(spec);
75
+ throw(error);
76
+ }
77
+ promisy.resolve.call(spec);
78
+ done();
79
+ });
80
+ return promisy;
81
+ };
82
+ testFn.call(this);
83
+ });
84
+
85
+ }
86
+
87
+ afterEach(function(){
88
+ MessageBus.stop()
89
+ if (MessageBus.longPoll){
90
+ MessageBus.longPoll.abort();
91
+ }
92
+ MessageBus.callbacks.splice(0, MessageBus.callbacks.length)
93
+ });
@@ -0,0 +1,77 @@
1
+ describe("Messagebus", function() {
2
+
3
+ it("submits change requests", function(done){
4
+ spyOn(this.MockedXMLHttpRequest.prototype, 'send').and.callThrough();
5
+ var spec = this;
6
+ MessageBus.subscribe('/test', function(){
7
+ expect(spec.MockedXMLHttpRequest.prototype.send)
8
+ .toHaveBeenCalled()
9
+ var req = JSON.parse(spec.MockedXMLHttpRequest.prototype.send.calls.argsFor(0)[0]);
10
+ expect(req['/test']).toEqual(-1)
11
+ expect(req['__seq']).not.toBeUndefined();
12
+ done()
13
+ });
14
+ });
15
+
16
+ it("calls callbacks", function(done){
17
+ MessageBus.subscribe('/test', function(message){
18
+ expect(message.password).toEqual('MessageBusRocks!');
19
+ done();
20
+ });
21
+ });
22
+
23
+ it('stores messages when paused, then delivers them when resumed', function(done){
24
+ MessageBus.pause()
25
+ spyOn(this.MockedXMLHttpRequest.prototype, 'send').and.callThrough();
26
+ var spec = this;
27
+ var onMessageSpy = jasmine.createSpy('onMessageSpy');
28
+ MessageBus.subscribe('/test', onMessageSpy);
29
+ setTimeout(function(){
30
+ expect(spec.MockedXMLHttpRequest.prototype.send).toHaveBeenCalled()
31
+ expect(onMessageSpy).not.toHaveBeenCalled()
32
+ MessageBus.resume()
33
+ }, 510) // greater than delayPollTimeout of 500
34
+ setTimeout(function(){
35
+ expect(onMessageSpy).toHaveBeenCalled()
36
+ done()
37
+ }, 550) // greater than first timeout above
38
+ });
39
+
40
+ it('can unsubscribe from callbacks', function(done){
41
+ var onMessageSpy = jasmine.createSpy('onMessageSpy');
42
+ MessageBus.subscribe('/test', onMessageSpy);
43
+ MessageBus.unsubscribe('/test', onMessageSpy);
44
+ MessageBus.subscribe('/test', function(){
45
+ expect(onMessageSpy).not.toHaveBeenCalled()
46
+ done()
47
+ });
48
+ });
49
+
50
+ testMB('sets dlp paramater when longPolling is disabled', function(){
51
+ MessageBus.enableLongPolling = false
52
+ this.perform(function(message, xhr){
53
+ expect(xhr.url).toMatch("dlp=t");
54
+ }).finally(function(){
55
+ MessageBus.enableLongPolling = true
56
+ })
57
+ });
58
+
59
+ testMB('respects baseUrl setting', function(){
60
+ MessageBus.baseUrl = "/a/test/base/url/";
61
+ this.perform(function(message, xhr){
62
+ expect(xhr.url).toMatch("/a/test/base/url/");
63
+ }).finally(function(){
64
+ MessageBus.baseUrl = "/";
65
+ })
66
+ });
67
+
68
+ it('removes itself from root namespace when noConflict is called', function(){
69
+ expect(window.MessageBus).not.toBeUndefined();
70
+ var mb = window.MessageBus;
71
+ expect(mb).toEqual(window.MessageBus.noConflict());
72
+ expect(window.MessageBus).toBeUndefined();
73
+ // reset it so afterEach has something to work on
74
+ window.MessageBus = mb;
75
+ });
76
+
77
+ });
@@ -0,0 +1,136 @@
1
+ # src_files
2
+ #
3
+ # Return an array of filepaths relative to src_dir to include before jasmine specs.
4
+ # Default: []
5
+ #
6
+ # EXAMPLE:
7
+ #
8
+ # src_files:
9
+ # - lib/source1.js
10
+ # - lib/source2.js
11
+ # - dist/**/*.js
12
+ #
13
+ src_files:
14
+ - assets/message-bus.js
15
+ - assets/message-bus-ajax.js
16
+
17
+
18
+ # stylesheets
19
+ #
20
+ # Return an array of stylesheet filepaths relative to src_dir to include before jasmine specs.
21
+ # Default: []
22
+ #
23
+ # EXAMPLE:
24
+ #
25
+ # stylesheets:
26
+ # - css/style.css
27
+ # - stylesheets/*.css
28
+ #
29
+ # stylesheets:
30
+ # - stylesheets/**/*.css
31
+
32
+ # helpers
33
+ #
34
+ # Return an array of filepaths relative to spec_dir to include before jasmine specs.
35
+ # Default: ["helpers/**/*.js"]
36
+ #
37
+ # EXAMPLE:
38
+ #
39
+ # helpers:
40
+ # - helpers/**/*.js
41
+ #
42
+ helpers:
43
+ - 'SpecHelper.js'
44
+
45
+ # spec_files
46
+ #
47
+ # Return an array of filepaths relative to spec_dir to include.
48
+ # Default: ["**/*[sS]pec.js"]
49
+ #
50
+ # EXAMPLE:
51
+ #
52
+ # spec_files:
53
+ # - **/*[sS]pec.js
54
+ #
55
+ spec_files:
56
+ - '**/*[sS]pec.js'
57
+
58
+ # src_dir
59
+ #
60
+ # Source directory path. Your src_files must be returned relative to this path. Will use root if left blank.
61
+ # Default: project root
62
+ #
63
+ # EXAMPLE:
64
+ #
65
+ # src_dir: public
66
+ #
67
+ src_dir:
68
+
69
+ # spec_dir
70
+ #
71
+ # Spec directory path. Your spec_files must be returned relative to this path.
72
+ # Default: spec/javascripts
73
+ #
74
+ # EXAMPLE:
75
+ #
76
+ # spec_dir: spec/javascripts
77
+ #
78
+ spec_dir: spec/assets
79
+
80
+ # spec_helper
81
+ #
82
+ # Ruby file that Jasmine server will require before starting.
83
+ # Returned relative to your root path
84
+ # Default spec/javascripts/support/jasmine_helper.rb
85
+ #
86
+ # EXAMPLE:
87
+ #
88
+ # spec_helper: spec/javascripts/support/jasmine_helper.rb
89
+ #
90
+ # spec_helper: spec/javascripts/support/jasmine_helper.rb
91
+
92
+ # boot_dir
93
+ #
94
+ # Boot directory path. Your boot_files must be returned relative to this path.
95
+ # Default: Built in boot file
96
+ #
97
+ # EXAMPLE:
98
+ #
99
+ # boot_dir: spec/javascripts/support/boot
100
+ #
101
+ boot_dir:
102
+
103
+ # boot_files
104
+ #
105
+ # Return an array of filepaths relative to boot_dir to include in order to boot Jasmine
106
+ # Default: Built in boot file
107
+ #
108
+ # EXAMPLE
109
+ #
110
+ # boot_files:
111
+ # - '**/*.js'
112
+ #
113
+ boot_files:
114
+
115
+ # rack_options
116
+ #
117
+ # Extra options to be passed to the rack server
118
+ # by default, Port and AccessLog are passed.
119
+ #
120
+ # This is an advanced options, and left empty by default
121
+ #
122
+ # EXAMPLE
123
+ #
124
+ # rack_options:
125
+ # server: 'thin'
126
+
127
+ # random
128
+ #
129
+ # Run specs in semi-random order.
130
+ # Default: false
131
+ #
132
+ # EXAMPLE:
133
+ #
134
+ # random: true
135
+ #
136
+ random:
@@ -38,7 +38,7 @@ describe MessageBus::Rack::Middleware do
38
38
 
39
39
  module LongPolling
40
40
  extend Minitest::Spec::DSL
41
-
41
+
42
42
  before do
43
43
  @bus.long_polling_enabled = true
44
44
  end
@@ -300,6 +300,15 @@ describe MessageBus::Rack::Middleware do
300
300
  parsed.length.must_equal 1
301
301
  end
302
302
 
303
+ it "can decode a JSON encoded request" do
304
+ id = @bus.last_id('/foo')
305
+ @bus.publish("/foo", {json: true})
306
+ post( "/message-bus/1234",
307
+ JSON.generate({'/foo' => id}),
308
+ { "CONTENT_TYPE" => "application/json" })
309
+ JSON.parse(last_response.body).first["data"].must_equal({'json' => true})
310
+ end
311
+
303
312
  describe "messagebus.channels env support" do
304
313
  let(:extra_middleware) do
305
314
  Class.new do
@@ -1,10 +1,11 @@
1
1
  /*jshint bitwise: false*/
2
- "use strict;"
2
+ (function(global, document, undefined) {
3
+ 'use strict';
4
+ var previousMessageBus = global.MessageBus;
3
5
 
4
- window.MessageBus = (function() {
5
6
  // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
6
7
  var callbacks, clientId, failCount, shouldLongPoll, queue, responseCallbacks, uniqueId, baseUrl;
7
- var me, started, stopped, longPoller, pollTimeout, paused, later;
8
+ var me, started, stopped, longPoller, pollTimeout, paused, later, jQuery, interval, chunkedBackoff;
8
9
 
9
10
  uniqueId = function() {
10
11
  return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
@@ -25,10 +26,9 @@ window.MessageBus = (function() {
25
26
  paused = false;
26
27
  later = [];
27
28
  chunkedBackoff = 0;
28
-
29
+ jQuery = global.jQuery;
29
30
  var hiddenProperty;
30
31
 
31
-
32
32
  (function(){
33
33
  var prefixes = ["","webkit","ms","moz"];
34
34
  for(var i=0; i<prefixes.length; i++) {
@@ -161,38 +161,55 @@ window.MessageBus = (function() {
161
161
  }
162
162
  };
163
163
 
164
+ var setOnProgressListener = function(xhr) {
165
+ var position = 0;
166
+ // if it takes longer than 3000 ms to get first chunk, we have some proxy
167
+ // this is messing with us, so just backoff from using chunked for now
168
+ var chunkedTimeout = setTimeout(disableChunked,3000);
169
+ xhr.onprogress = function () {
170
+ clearTimeout(chunkedTimeout);
171
+ if(xhr.getResponseHeader('Content-Type') === 'application/json; charset=utf-8') {
172
+ // not chunked we are sending json back
173
+ chunked = false;
174
+ return;
175
+ }
176
+ position = handle_progress(xhr.responseText, position);
177
+ }
178
+ };
179
+ if (!me.ajax){
180
+ throw new Error("Either jQuery or the ajax adapter must be loaded");
181
+ }
164
182
  var req = me.ajax({
165
- url: me.baseUrl + "message-bus/" + me.clientId + "/poll?" + (!longPoll ? "dlp=t" : ""),
183
+ url: me.baseUrl + "message-bus/" + me.clientId + "/poll" + (!longPoll ? "?dlp=t" : ""),
166
184
  data: data,
167
185
  cache: false,
168
186
  dataType: dataType,
169
187
  type: 'POST',
170
188
  headers: headers,
189
+ messageBus: {
190
+ chunked: chunked,
191
+ onProgressListener: function(xhr) {
192
+ var position = 0;
193
+ // if it takes longer than 3000 ms to get first chunk, we have some proxy
194
+ // this is messing with us, so just backoff from using chunked for now
195
+ var chunkedTimeout = setTimeout(disableChunked,3000);
196
+ return xhr.onprogress = function () {
197
+ clearTimeout(chunkedTimeout);
198
+ if(xhr.getResponseHeader('Content-Type') === 'application/json; charset=utf-8') {
199
+ chunked = false; // not chunked, we are sending json back
200
+ } else {
201
+ position = handle_progress(xhr.responseText, position);
202
+ }
203
+ }
204
+ }
205
+ },
171
206
  xhr: function() {
172
- var xhr = jQuery.ajaxSettings.xhr();
173
-
174
- if (!chunked) {
175
- return xhr;
176
- }
177
-
178
- var position = 0;
179
-
180
- // if it takes longer than 3000 ms to get first chunk, we have some proxy
181
- // this is messing with us, so just backoff from using chunked for now
182
- var chunkedTimeout = setTimeout(disableChunked,3000);
183
-
184
- xhr.onprogress = function () {
185
- clearTimeout(chunkedTimeout);
186
-
187
- if(xhr.getResponseHeader('Content-Type') === 'application/json; charset=utf-8') {
188
- // not chunked we are sending json back
189
- chunked = false;
190
- return;
191
- }
192
- position = handle_progress(xhr.responseText, position);
193
- }
194
-
195
- return xhr;
207
+ var xhr = jQuery.ajaxSettings.xhr();
208
+ if (!chunked) {
209
+ return xhr;
210
+ }
211
+ this.messageBus.onProgressListener(xhr);
212
+ return xhr;
196
213
  },
197
214
  success: function(messages) {
198
215
  if (!chunked) {
@@ -257,10 +274,11 @@ window.MessageBus = (function() {
257
274
  clientId: clientId,
258
275
  alwaysLongPoll: false,
259
276
  baseUrl: baseUrl,
260
- // TODO we can make the dependency on $ and jQuery conditional
261
- // all we really need is an implementation of ajax
262
- ajax: ($ && $.ajax),
263
-
277
+ ajax: (jQuery && jQuery.ajax),
278
+ noConflict: function(){
279
+ global.MessageBus = global.MessageBus.previousMessageBus;
280
+ return this;
281
+ },
264
282
  diagnostics: function(){
265
283
  console.log("Stopped: " + stopped + " Started: " + started);
266
284
  console.log("Current callbacks");
@@ -317,7 +335,7 @@ window.MessageBus = (function() {
317
335
 
318
336
  // monitor visibility, issue a new long poll when the page shows
319
337
  if(document.addEventListener && 'hidden' in document){
320
- me.visibilityEvent = document.addEventListener('visibilitychange', function(){
338
+ me.visibilityEvent = global.document.addEventListener('visibilitychange', function(){
321
339
  if(!document.hidden && !me.longPoll && pollTimeout){
322
340
  clearTimeout(pollTimeout);
323
341
  pollTimeout = null;
@@ -366,7 +384,7 @@ window.MessageBus = (function() {
366
384
 
367
385
  for (var i=callbacks.length-1; i>=0; i--) {
368
386
 
369
- callback = callbacks[i];
387
+ var callback = callbacks[i];
370
388
  var keep;
371
389
 
372
390
  if (glob) {
@@ -392,6 +410,5 @@ window.MessageBus = (function() {
392
410
  return removed;
393
411
  }
394
412
  };
395
-
396
- return me;
397
- })();
413
+ global.MessageBus = me;
414
+ })(window, document);
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: message_bus
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.beta.5
4
+ version: 2.0.0.beta.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-11 00:00:00.000000000 Z
11
+ date: 2016-03-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: jasmine
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  description: A message bus for rack
56
70
  email:
57
71
  - sam.saffron@gmail.com
@@ -73,6 +87,7 @@ files:
73
87
  - assets/handlebars.js
74
88
  - assets/index.handlebars
75
89
  - assets/jquery-1.8.2.js
90
+ - assets/message-bus-ajax.js
76
91
  - assets/message-bus.js
77
92
  - examples/bench/bench.lua
78
93
  - examples/bench/config.ru
@@ -102,6 +117,9 @@ files:
102
117
  - lib/message_bus/timer_thread.rb
103
118
  - lib/message_bus/version.rb
104
119
  - message_bus.gemspec
120
+ - spec/assets/SpecHelper.js
121
+ - spec/assets/message-bus.spec.js
122
+ - spec/assets/support/jasmine.yml
105
123
  - spec/lib/fake_async_middleware.rb
106
124
  - spec/lib/message_bus/assets/asset_encoding_spec.rb
107
125
  - spec/lib/message_bus/backends/postgres_spec.rb
@@ -139,6 +157,9 @@ signing_key:
139
157
  specification_version: 4
140
158
  summary: ''
141
159
  test_files:
160
+ - spec/assets/SpecHelper.js
161
+ - spec/assets/message-bus.spec.js
162
+ - spec/assets/support/jasmine.yml
142
163
  - spec/lib/fake_async_middleware.rb
143
164
  - spec/lib/message_bus/assets/asset_encoding_spec.rb
144
165
  - spec/lib/message_bus/backends/postgres_spec.rb