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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

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