message_bus 2.1.5 → 2.1.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
  SHA256:
3
- metadata.gz: bc63e91243c887e17566c8e993d8a1974095fb5dbd526f975532a49cc21b53df
4
- data.tar.gz: 58721c57100d84323d839c9c6ffbb9dd49a86e4f31e241058bf89c6c88224646
3
+ metadata.gz: 41c92c01f6f8f8b478877cd97213ceaa9517b28eddb42edc6e421a39d8e6da50
4
+ data.tar.gz: 00bab454cbce2f3f264eda6470ade5d61bfde42ddbc8709d288da6ee2c204dd8
5
5
  SHA512:
6
- metadata.gz: 996048cdb5e4f4834ec943fbd9dad61b40e5a1c2977046a426c3ec1ec621ba8990d272ec21f916e2d098f7096489d516bdc56d0bfb0dc1d859c2759f42bf83c6
7
- data.tar.gz: da9e30f25a9918d7d2290a508e6d068d93ec18a5f8d69423426c381838c00dcd7b8480d5a1760196ceb66ea7bb30da209b6c7046f8a2c74fc3a8322d40a730dc
6
+ metadata.gz: e009850b2ecd51ed40978993297b67f70e2cb50939e46f165630ce5aa3c1b42170e7ca70f753888218c00ec7fb33dd6ece38900d263f683e2183a66a7cbf0e7a
7
+ data.tar.gz: 435044844fc8f8620d3ddbac25c5cae390e4ee2988cca28f37fa41da18f12b57293ef7686ecd412af3be23a5a9787e29c459d7bd52548fd1a7bf5378956d5a80
data/CHANGELOG CHANGED
@@ -1,3 +1,12 @@
1
+ 15-10-2018
2
+
3
+ - Version 2.1.6
4
+
5
+ - FEATURE: `MesssageBus.publish` accepts option `site_id` to publish to a site
6
+ - FEATURE: Added MessageBus::DistributedCache for cross process caching
7
+ - PERF: Use monotonic times in timer thread
8
+ - FEATURE: min poll interval is now configurable client side
9
+
1
10
  16-05-2018
2
11
 
3
12
  - Version 2.1.5
data/Gemfile CHANGED
@@ -5,6 +5,7 @@ gemspec
5
5
 
6
6
  group :test do
7
7
  gem 'minitest'
8
+ gem 'minitest-hooks'
8
9
  gem 'rake'
9
10
  gem 'http_parser.rb'
10
11
  gem 'thin'
@@ -14,3 +15,4 @@ end
14
15
 
15
16
  gem 'redis'
16
17
  gem 'rack'
18
+ gem 'concurrent-ruby' # for distributed-cache
data/README.md CHANGED
@@ -14,7 +14,6 @@ Live chat demo per [examples/chat](https://github.com/SamSaffron/message_bus/tre
14
14
 
15
15
  ### http://chat.samsaffron.com
16
16
 
17
-
18
17
  ## Want to help?
19
18
 
20
19
  If you are looking to contribute to this project here are some ideas
@@ -119,6 +118,18 @@ MessageBus.user_id_lookup do |env|
119
118
  end
120
119
  ```
121
120
 
121
+ ### Debugging
122
+
123
+ When setting up MessageBus, it's good to manually check the channels before integrating the client.
124
+
125
+ You can `curl` MessageBus. This is helpful when trying to debug what may be doing wrong. This uses https://chat.samsaffron.com.
126
+
127
+ ```
128
+ curl -H "Content-Type: application/x-www-form-urlencoded" -X POST --data "/message=0" https://chat.samsaffron.com/message-bus/client-id/poll\?dlp\=t
129
+ ```
130
+
131
+ You should see a reply with the messages of that channel you requested for (`/message`) starting at the message ID (`0`). `dlp=t` disables long-polling: we do not want this request to stay open.
132
+
122
133
  ### Transport
123
134
 
124
135
  MessageBus ships with 3 transport mechanisms.
@@ -165,7 +176,8 @@ Polling also requires no special setup, MessageBus will fallback to polling afte
165
176
  MessageBus can be used in an environment that hosts multiple sites by multiplexing channels. To use this mode
166
177
 
167
178
  ```ruby
168
- # define a site_id lookup method
179
+ # define a site_id lookup method, which is executed
180
+ # when `MessageBus.publish` is called
169
181
  MessageBus.configure(site_id_lookup: proc do
170
182
  some_method_that_returns_site_id_string
171
183
  end)
@@ -173,6 +185,11 @@ end)
173
185
  # you may post messages just to this site
174
186
  MessageBus.publish "/channel", "some message"
175
187
 
188
+ # you can also choose to pass the `site_id`.
189
+ # This takes precendence over whatever `site_id_lookup`
190
+ # returns
191
+ MessageBus.publish "/channel", "some message", site_id: "site-id"
192
+
176
193
  # you may publish messages to ALL sites using the /global/ prefix
177
194
  MessageBus.publish "/global/channel", "will go to all sites"
178
195
 
@@ -238,6 +255,7 @@ Setting|Default|Info
238
255
  enableLongPolling|true|Allow long-polling (provided it is enable by the server)
239
256
  callbackInterval|15000|Safeguard to ensure background polling does not exceed this interval (in milliseconds)
240
257
  backgroundCallbackInterval|60000|Interval to poll when long polling is disabled (either explicitly or due to browser being in background)
258
+ minPollInterval|100|When polling requests succeed, this is the minimum amount of time to wait before making the next request.
241
259
  maxPollInterval|180000|If request to the server start failing, MessageBus will backoff, this is the upper limit of the backoff.
242
260
  alwaysLongPoll|false|For debugging you may want to disable the "is browser in background" check and always long-poll
243
261
  baseUrl|/|If message bus is mounted in a subdirectory of different domain, you may configure it to perform requests there
@@ -294,13 +312,13 @@ This is configurable via accessors on the ReliablePubSub instance.
294
312
 
295
313
  ```ruby
296
314
  # only store 100 messages per channel
297
- MessageBus.reliabe_pub_sub.max_backlog_size = 100
315
+ MessageBus.reliable_pub_sub.max_backlog_size = 100
298
316
 
299
317
  # only store 100 global messages
300
- MessageBus.reliabe_pub_sub.max_global_backlog_size = 100
318
+ MessageBus.reliable_pub_sub.max_global_backlog_size = 100
301
319
 
302
320
  # flush per-channel backlog after 100 seconds of inactivity
303
- MessageBus.reliabe_pub_sub.max_backlog_age = 100
321
+ MessageBus.reliable_pub_sub.max_backlog_age = 100
304
322
 
305
323
  ```
306
324
 
@@ -395,6 +413,52 @@ Rails.application.config do |config|
395
413
  end
396
414
  ```
397
415
 
416
+ ### A Distributed Cache
417
+
418
+ MessageBus ships with an optional DistributedCache object you can use to synchronize a cache between processes.
419
+ It allows you a simple and efficient way of synchronizing a cache between processes.
420
+
421
+ ```ruby
422
+ require 'message_bus/distributed_cache'
423
+
424
+ # process 1
425
+
426
+ cache = MessageBus::DistributedCache.new("animals")
427
+
428
+ # process 2
429
+
430
+ cache = MessageBus::DistributedCache.new("animals")
431
+
432
+ # process 1
433
+
434
+ cache["frogs"] = 5
435
+
436
+ # process 2
437
+
438
+ puts cache["frogs"]
439
+ # => 5
440
+
441
+ cache["frogs"] = nil
442
+
443
+ # process 1
444
+
445
+ puts cache["frogs"]
446
+ # => nil
447
+
448
+ ```
449
+
450
+ Automatically expiring the cache on app update:
451
+
452
+ ```ruby
453
+ cache = MessageBus::DistributedCache.new("cache name", app_version: "12.1.7.ABDEB")
454
+ cache["a"] = 77
455
+
456
+ cache = MessageBus::DistributedCache.new("cache name", app_version: "12.1.7.ABDEF")
457
+
458
+ puts cache["a"]
459
+ # => nil
460
+ ```
461
+
398
462
  #### Error Handling
399
463
 
400
464
  The internet is a chaotic environment and clients can drop off for a variety of reasons. If this happens while MessageBus is trying to write a message to the client you may see something like this in your logs:
@@ -6,6 +6,7 @@
6
6
  // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
7
7
  var callbacks, clientId, failCount, shouldLongPoll, queue, responseCallbacks, uniqueId, baseUrl;
8
8
  var me, started, stopped, longPoller, pollTimeout, paused, later, jQuery, interval, chunkedBackoff;
9
+ var delayPollTimeout;
9
10
 
10
11
  var ajaxInProgress = false;
11
12
 
@@ -277,7 +278,7 @@
277
278
  var interval;
278
279
  try {
279
280
  if (gotData || aborted) {
280
- interval = 100;
281
+ interval = me.minPollInterval;
281
282
  } else {
282
283
  interval = me.callbackInterval;
283
284
  if (failCount > 2) {
@@ -303,11 +304,16 @@
303
304
 
304
305
  if (pollTimeout) {
305
306
  clearTimeout(pollTimeout);
306
- }
307
- pollTimeout = setTimeout(function(){
308
307
  pollTimeout = null;
309
- poll();
310
- }, interval);
308
+ }
309
+
310
+ if (started) {
311
+ pollTimeout = setTimeout(function(){
312
+ pollTimeout = null;
313
+ poll();
314
+ }, interval);
315
+ }
316
+
311
317
  me.longPoll = null;
312
318
  }
313
319
  });
@@ -322,6 +328,7 @@
322
328
  enableLongPolling: true,
323
329
  callbackInterval: 15000,
324
330
  backgroundCallbackInterval: 60000,
331
+ minPollInterval: 100,
325
332
  maxPollInterval: 3 * 60 * 1000,
326
333
  callbacks: callbacks,
327
334
  clientId: clientId,
@@ -354,11 +361,18 @@
354
361
  stop: function() {
355
362
  stopped = true;
356
363
  started = false;
364
+ if (delayPollTimeout) {
365
+ clearTimeout(delayPollTimeout);
366
+ delayPollTimeout = null;
367
+ }
368
+ if (me.longPoll) {
369
+ me.longPoll.abort();
370
+ }
357
371
  },
358
372
 
359
373
  // Start polling
360
374
  start: function() {
361
- var poll, delayPollTimeout;
375
+ var poll;
362
376
 
363
377
  if (started) return;
364
378
  started = true;
data/lib/message_bus.rb CHANGED
@@ -226,10 +226,12 @@ module MessageBus::Implementation
226
226
  group_ids = nil
227
227
  client_ids = nil
228
228
 
229
+ site_id = nil
229
230
  if opts
230
231
  user_ids = opts[:user_ids]
231
232
  group_ids = opts[:group_ids]
232
233
  client_ids = opts[:client_ids]
234
+ site_id = opts[:site_id]
233
235
  end
234
236
 
235
237
  raise ::MessageBus::InvalidMessage if (user_ids || group_ids) && global?(channel)
@@ -250,7 +252,8 @@ module MessageBus::Implementation
250
252
  }
251
253
  end
252
254
 
253
- reliable_pub_sub.publish(encode_channel_name(channel), encoded_data, channel_opts)
255
+ encoded_channel_name = encode_channel_name(channel, site_id)
256
+ reliable_pub_sub.publish(encoded_channel_name, encoded_data, channel_opts)
254
257
  end
255
258
 
256
259
  def blocking_subscribe(channel = nil, &blk)
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Like a hash, just does its best to stay in sync across the farm.
4
+ # On boot all instances are blank, but they populate as various processes
5
+ # fill it up.
6
+
7
+ require 'weakref'
8
+ require 'base64'
9
+ require 'securerandom'
10
+
11
+ module MessageBus
12
+ class DistributedCache
13
+
14
+ DEFAULT_SITE_ID = 'default'
15
+
16
+ class Manager
17
+ CHANNEL_NAME ||= '/distributed_hash'.freeze
18
+
19
+ attr_accessor :app_version
20
+
21
+ def initialize(message_bus = nil)
22
+ @subscribers = []
23
+ @subscribed = false
24
+ @lock = Mutex.new
25
+ @message_bus = message_bus || MessageBus
26
+ end
27
+
28
+ def subscribers
29
+ @subscribers
30
+ end
31
+
32
+ def process_message(message)
33
+ i = @subscribers.length - 1
34
+
35
+ payload = message.data
36
+
37
+ while i >= 0
38
+ begin
39
+ current = @subscribers[i]
40
+
41
+ next if payload["origin"] == current.identity
42
+ next if current.key != payload["hash_key"]
43
+
44
+ next if @app_version && payload["app_version"] != @app_version
45
+
46
+ hash = current.hash(message.site_id || DEFAULT_SITE_ID)
47
+
48
+ case payload["op"]
49
+ when "set" then hash[payload["key"]] = payload["marshalled"] ? Marshal.load(Base64.decode64(payload["value"])) : payload["value"]
50
+ when "delete" then hash.delete(payload["key"])
51
+ when "clear" then hash.clear
52
+ end
53
+
54
+ rescue WeakRef::RefError
55
+ @subscribers.delete_at(i)
56
+ ensure
57
+ i -= 1
58
+ end
59
+ end
60
+ end
61
+
62
+ def ensure_subscribe!
63
+ return if @subscribed
64
+ @lock.synchronize do
65
+ return if @subscribed
66
+ @message_bus.subscribe(CHANNEL_NAME) do |message|
67
+ @lock.synchronize do
68
+ process_message(message)
69
+ end
70
+ end
71
+ @subscribed = true
72
+ end
73
+ end
74
+
75
+ def publish(hash, message)
76
+ message[:origin] = hash.identity
77
+ message[:hash_key] = hash.key
78
+ message[:app_version] = @app_version if @app_version
79
+ @message_bus.publish(CHANNEL_NAME, message, user_ids: [-1])
80
+ end
81
+
82
+ def set(hash, key, value)
83
+ # special support for set
84
+ marshal = (Set === value || Hash === value || Array === value)
85
+ value = Base64.encode64(Marshal.dump(value)) if marshal
86
+ publish(hash, op: :set, key: key, value: value, marshalled: marshal)
87
+ end
88
+
89
+ def delete(hash, key)
90
+ publish(hash, op: :delete, key: key)
91
+ end
92
+
93
+ def clear(hash)
94
+ publish(hash, op: :clear)
95
+ end
96
+
97
+ def register(hash)
98
+ @lock.synchronize do
99
+ @subscribers << WeakRef.new(hash)
100
+ end
101
+ end
102
+ end
103
+
104
+ @default_manager = Manager.new
105
+
106
+ def self.default_manager
107
+ @default_manager
108
+ end
109
+
110
+ attr_reader :key
111
+
112
+ def initialize(key, manager: nil, namespace: true, app_version: nil)
113
+ @key = key
114
+ @data = {}
115
+ @manager = manager || DistributedCache.default_manager
116
+ @manager.app_version = app_version if app_version
117
+ @namespace = namespace
118
+ @app_version = app_version
119
+
120
+ @manager.ensure_subscribe!
121
+ @manager.register(self)
122
+ end
123
+
124
+ def identity
125
+ # fork resilient / multi machine identity
126
+ (@seed_id ||= SecureRandom.hex) + "#{Process.pid}"
127
+ end
128
+
129
+ def []=(k, v)
130
+ k = k.to_s if Symbol === k
131
+ @manager.set(self, k, v)
132
+ hash[k] = v
133
+ end
134
+
135
+ def [](k)
136
+ k = k.to_s if Symbol === k
137
+ hash[k]
138
+ end
139
+
140
+ def delete(k, publish: true)
141
+ k = k.to_s if Symbol === k
142
+ @manager.delete(self, k) if publish
143
+ hash.delete(k)
144
+ end
145
+
146
+ def clear
147
+ @manager.clear(self)
148
+ hash.clear
149
+ end
150
+
151
+ def hash(site_id_arg = nil)
152
+ site_id =
153
+ if @namespace
154
+ site_id_arg ||
155
+ (MessageBus.site_id_lookup && MessageBus.site_id_lookup.call) ||
156
+ DEFAULT_SITE_ID
157
+ else
158
+ DEFAULT_SITE_ID
159
+ end
160
+
161
+ @data[site_id] ||= {}
162
+ end
163
+
164
+ end
165
+ end
@@ -4,7 +4,14 @@ class MessageBus::TimerThread
4
4
  attr_reader :jobs
5
5
 
6
6
  class Cancelable
7
- NOOP = proc {}
7
+ class NoOp
8
+ def call
9
+ end
10
+ end
11
+
12
+ # usually you could just use a blank lambda
13
+ # but an object is ever so slightly faster
14
+ NOOP = NoOp.new
8
15
 
9
16
  def initialize(job)
10
17
  @job = job
@@ -58,7 +65,7 @@ class MessageBus::TimerThread
58
65
 
59
66
  # queue a block to run after a certain delay (in seconds)
60
67
  def queue(delay = 0, &block)
61
- queue_time = Time.new.to_f + delay
68
+ queue_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + delay
62
69
  job = [queue_time, block]
63
70
 
64
71
  @mutex.synchronize do
@@ -96,7 +103,7 @@ class MessageBus::TimerThread
96
103
 
97
104
  def do_work
98
105
  while !@stopped
99
- if @next && @next <= Time.new.to_f
106
+ if @next && @next <= Process.clock_gettime(Process::CLOCK_MONOTONIC)
100
107
  _, blk = @mutex.synchronize { @jobs.shift }
101
108
  begin
102
109
  blk.call
@@ -107,10 +114,10 @@ class MessageBus::TimerThread
107
114
  @next, _ = @jobs[0]
108
115
  end
109
116
  end
110
- unless @next && @next <= Time.new.to_f
117
+ unless @next && @next <= Process.clock_gettime(Process::CLOCK_MONOTONIC)
111
118
  sleep_time = 1000
112
119
  @mutex.synchronize do
113
- sleep_time = @next - Time.new.to_f if @next
120
+ sleep_time = @next - Process.clock_gettime(Process::CLOCK_MONOTONIC) if @next
114
121
  end
115
122
  sleep [0, sleep_time].max
116
123
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module MessageBus
3
- VERSION = "2.1.5"
3
+ VERSION = "2.1.6"
4
4
  end
@@ -27,6 +27,7 @@ beforeEach(function () {
27
27
  function MockedXMLHttpRequest(){
28
28
  this.headers = {};
29
29
  };
30
+
30
31
  MockedXMLHttpRequest.prototype.send = function(){
31
32
  this.readyState = 4
32
33
  this.responseText = encodeChunks(this, spec.responseChunks);
@@ -35,21 +36,38 @@ beforeEach(function () {
35
36
  if (this.onprogress){ this.onprogress(); }
36
37
  this.onreadystatechange()
37
38
  }
39
+
38
40
  MockedXMLHttpRequest.prototype.open = function(){ }
39
- MockedXMLHttpRequest.prototype.abort = function(){ }
41
+
42
+ MockedXMLHttpRequest.prototype.abort = function(){
43
+ this.readyState = 4
44
+ this.responseText = '';
45
+ this.statusText = '';
46
+ this.status = 400;
47
+ this.onreadystatechange()
48
+ }
49
+
40
50
  MockedXMLHttpRequest.prototype.setRequestHeader = function(k,v){
41
51
  this.headers[k] = v;
42
52
  }
53
+
43
54
  MockedXMLHttpRequest.prototype.getResponseHeader = function(){
44
55
  return 'text/plain; charset=utf-8';
45
56
  }
57
+
46
58
  MessageBus.xhrImplementation = MockedXMLHttpRequest
47
59
  this.MockedXMLHttpRequest = MockedXMLHttpRequest
48
- MessageBus.start()
60
+
49
61
  this.responseChunks = [
50
62
  {channel: '/test', data: {password: 'MessageBusRocks!'}}
51
63
  ];
52
64
 
65
+ MessageBus.start();
66
+ });
67
+
68
+ afterEach(function(){
69
+ MessageBus.stop()
70
+ MessageBus.callbacks.splice(0, MessageBus.callbacks.length)
53
71
  });
54
72
 
55
73
  window.testMB = function(description, testFn, path, data){
@@ -88,10 +106,3 @@ window.testMB = function(description, testFn, path, data){
88
106
 
89
107
  }
90
108
 
91
- afterEach(function(){
92
- MessageBus.stop()
93
- if (MessageBus.longPoll){
94
- MessageBus.longPoll.abort();
95
- }
96
- MessageBus.callbacks.splice(0, MessageBus.callbacks.length)
97
- });
@@ -84,6 +84,12 @@ describe("Messagebus", function() {
84
84
  window.MessageBus = mb;
85
85
  });
86
86
 
87
+ it('respects minPollInterval setting with defaults', function(){
88
+ expect(MessageBus.minPollInterval).toEqual(100);
89
+ MessageBus.minPollInterval = 1000;
90
+ expect(MessageBus.minPollInterval).toEqual(1000);
91
+ });
92
+
87
93
  testMB('sends using custom header', function(){
88
94
  MessageBus.headers['X-MB-TEST-VALUE'] = '42';
89
95
  this.perform(function(message, xhr){
@@ -0,0 +1,133 @@
1
+ require_relative '../../spec_helper'
2
+ require 'minitest/hooks/default'
3
+ require 'message_bus'
4
+ require 'message_bus/distributed_cache'
5
+
6
+ describe MessageBus::DistributedCache do
7
+
8
+ before :all do
9
+ @bus = MessageBus::Instance.new
10
+ @bus.configure(backend: :memory)
11
+ @manager = MessageBus::DistributedCache::Manager.new(@bus)
12
+ end
13
+
14
+ after :all do
15
+ @bus.destroy
16
+ end
17
+
18
+ def cache(name)
19
+ MessageBus::DistributedCache.new(name, manager: @manager)
20
+ end
21
+
22
+ let :cache_name do
23
+ SecureRandom.hex
24
+ end
25
+
26
+ before do
27
+ @cache1 = cache(cache_name)
28
+ @cache2 = cache(cache_name)
29
+ end
30
+
31
+ it 'supports arrays with hashes' do
32
+
33
+ c1 = cache("test1")
34
+ c2 = cache("test1")
35
+
36
+ c1["test"] = [{ test: :test }]
37
+
38
+ wait_for do
39
+ c2["test"] == [{ test: :test }]
40
+ end
41
+
42
+ expect(c2[:test]).must_equal([{ test: :test }])
43
+ end
44
+
45
+ it 'allows us to store Set' do
46
+ c1 = cache("test1")
47
+ c2 = cache("test1")
48
+
49
+ set = Set.new
50
+ set << 1
51
+ set << "b"
52
+ set << 92803984
53
+ set << 93739739873973
54
+
55
+ c1["cats"] = set
56
+
57
+ wait_for do
58
+ c2["cats"] == set
59
+ end
60
+
61
+ expect(c2["cats"]).must_equal(set)
62
+
63
+ set << 5
64
+
65
+ c2["cats"] = set
66
+
67
+ wait_for do
68
+ c1["cats"] == set
69
+ end
70
+
71
+ expect(c1["cats"]).must_equal(set)
72
+ end
73
+
74
+ it 'does not leak state across caches' do
75
+ c2 = cache("test1")
76
+ c3 = cache("test1")
77
+ c2["hi"] = "hi"
78
+ wait_for do
79
+ c3["hi"] == "hi"
80
+ end
81
+
82
+ Thread.pass
83
+ assert_nil(@cache1["hi"])
84
+
85
+ end
86
+
87
+ it 'allows coerces symbol keys to strings' do
88
+ @cache1[:key] = "test"
89
+ expect(@cache1["key"]).must_equal("test")
90
+
91
+ wait_for do
92
+ @cache2[:key] == "test"
93
+ end
94
+ expect(@cache2["key"]).must_equal("test")
95
+ end
96
+
97
+ it 'sets other caches' do
98
+ @cache1["test"] = "world"
99
+ wait_for do
100
+ @cache2["test"] == "world"
101
+ end
102
+ end
103
+
104
+ it 'deletes from other caches' do
105
+ @cache1["foo"] = "bar"
106
+
107
+ wait_for do
108
+ @cache2["foo"] == "bar"
109
+ end
110
+
111
+ @cache1.delete("foo")
112
+ assert_nil(@cache1["foo"])
113
+
114
+ wait_for do
115
+ @cache2["foo"] == nil
116
+ end
117
+ end
118
+
119
+ it 'clears cache on request' do
120
+ @cache1["foo"] = "bar"
121
+
122
+ wait_for do
123
+ @cache2["foo"] == "bar"
124
+ end
125
+
126
+ @cache1.clear
127
+ assert_nil(@cache1["foo"])
128
+ wait_for do
129
+ @cache2["boom"] == nil
130
+ end
131
+ end
132
+
133
+ end
@@ -13,7 +13,7 @@ describe MessageBus::TimerThread do
13
13
  it "allows you to queue every jobs" do
14
14
  i = 0
15
15
  m = Mutex.new
16
- every = @timer.every(0.001){m.synchronize{i += 1 if i < 3}}
16
+ every = @timer.every(0.001) { m.synchronize { i += 1 if i < 3 } }
17
17
  # allow lots of time, cause in test mode stuff can be slow
18
18
  wait_for(1000) do
19
19
  m.synchronize do
@@ -27,7 +27,7 @@ describe MessageBus::TimerThread do
27
27
 
28
28
  it "allows you to cancel timers" do
29
29
  success = true
30
- @timer.queue(0.005){success=false}.cancel
30
+ @timer.queue(0.005) { success = false }.cancel
31
31
  sleep(0.006)
32
32
  success.must_equal true
33
33
  end
@@ -45,7 +45,7 @@ describe MessageBus::TimerThread do
45
45
  4 == results.length
46
46
  }
47
47
 
48
- results.must_equal [0,1,2,3]
48
+ results.must_equal [0, 1, 2, 3]
49
49
  end
50
50
 
51
51
  it "should call the error callback if something goes wrong" do
@@ -159,6 +159,26 @@ describe MessageBus do
159
159
  assert_nil @bus.last_message("/nothing")
160
160
  end
161
161
 
162
+ describe "#publish" do
163
+ it "allows publishing to a explicit site" do
164
+ data, site_id, channel = nil
165
+
166
+ @bus.subscribe do |msg|
167
+ data = msg.data
168
+ site_id = msg.site_id
169
+ channel = msg.channel
170
+ end
171
+
172
+ @bus.publish("/chuck", "norris", site_id: "law-and-order")
173
+
174
+ wait_for(2000) { data }
175
+
176
+ data.must_equal 'norris'
177
+ site_id.must_equal 'law-and-order'
178
+ channel.must_equal '/chuck'
179
+ end
180
+ end
181
+
162
182
  describe "global subscriptions" do
163
183
  before do
164
184
  seq = 0
data/spec/spec_helper.rb CHANGED
@@ -16,7 +16,7 @@ if backend == :postgres
16
16
  end
17
17
  puts "Running with backend: #{backend}"
18
18
 
19
- def wait_for(timeout_milliseconds)
19
+ def wait_for(timeout_milliseconds=2000)
20
20
  timeout = (timeout_milliseconds + 0.0) / 1000
21
21
  finish = Time.now + timeout
22
22
 
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.1.5
4
+ version: 2.1.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: 2018-05-16 00:00:00.000000000 Z
11
+ date: 2018-10-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -95,6 +95,7 @@ files:
95
95
  - lib/message_bus/client.rb
96
96
  - lib/message_bus/connection_manager.rb
97
97
  - lib/message_bus/diagnostics.rb
98
+ - lib/message_bus/distributed_cache.rb
98
99
  - lib/message_bus/em_ext.rb
99
100
  - lib/message_bus/message.rb
100
101
  - lib/message_bus/rack/diagnostics.rb
@@ -114,6 +115,7 @@ files:
114
115
  - spec/lib/message_bus/backends/redis_spec.rb
115
116
  - spec/lib/message_bus/client_spec.rb
116
117
  - spec/lib/message_bus/connection_manager_spec.rb
118
+ - spec/lib/message_bus/distributed_cache_spec.rb
117
119
  - spec/lib/message_bus/multi_process_spec.rb
118
120
  - spec/lib/message_bus/rack/middleware_spec.rb
119
121
  - spec/lib/message_bus/timer_thread_spec.rb
@@ -157,6 +159,7 @@ test_files:
157
159
  - spec/lib/message_bus/backends/redis_spec.rb
158
160
  - spec/lib/message_bus/client_spec.rb
159
161
  - spec/lib/message_bus/connection_manager_spec.rb
162
+ - spec/lib/message_bus/distributed_cache_spec.rb
160
163
  - spec/lib/message_bus/multi_process_spec.rb
161
164
  - spec/lib/message_bus/rack/middleware_spec.rb
162
165
  - spec/lib/message_bus/timer_thread_spec.rb