message_bus 2.1.6 → 2.2.0.pre

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.

Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +13 -92
  3. data/.rubocop_todo.yml +659 -0
  4. data/.travis.yml +1 -1
  5. data/CHANGELOG +61 -0
  6. data/Dockerfile +18 -0
  7. data/Gemfile +3 -1
  8. data/Guardfile +0 -1
  9. data/README.md +188 -101
  10. data/Rakefile +12 -1
  11. data/assets/message-bus.js +1 -1
  12. data/docker-compose.yml +46 -0
  13. data/examples/bench/config.ru +8 -9
  14. data/examples/bench/unicorn.conf.rb +1 -1
  15. data/examples/chat/chat.rb +150 -153
  16. data/examples/minimal/config.ru +2 -3
  17. data/lib/message_bus.rb +224 -36
  18. data/lib/message_bus/backends.rb +7 -0
  19. data/lib/message_bus/backends/base.rb +184 -0
  20. data/lib/message_bus/backends/memory.rb +304 -226
  21. data/lib/message_bus/backends/postgres.rb +359 -318
  22. data/lib/message_bus/backends/redis.rb +380 -337
  23. data/lib/message_bus/client.rb +99 -41
  24. data/lib/message_bus/connection_manager.rb +29 -21
  25. data/lib/message_bus/diagnostics.rb +50 -41
  26. data/lib/message_bus/distributed_cache.rb +5 -7
  27. data/lib/message_bus/message.rb +2 -2
  28. data/lib/message_bus/rack/diagnostics.rb +65 -55
  29. data/lib/message_bus/rack/middleware.rb +64 -44
  30. data/lib/message_bus/rack/thin_ext.rb +13 -9
  31. data/lib/message_bus/rails/railtie.rb +2 -0
  32. data/lib/message_bus/timer_thread.rb +2 -2
  33. data/lib/message_bus/version.rb +2 -1
  34. data/message_bus.gemspec +3 -2
  35. data/spec/assets/support/jasmine_helper.rb +1 -1
  36. data/spec/lib/fake_async_middleware.rb +1 -6
  37. data/spec/lib/message_bus/assets/asset_encoding_spec.rb +3 -3
  38. data/spec/lib/message_bus/backend_spec.rb +409 -0
  39. data/spec/lib/message_bus/client_spec.rb +8 -11
  40. data/spec/lib/message_bus/connection_manager_spec.rb +8 -14
  41. data/spec/lib/message_bus/distributed_cache_spec.rb +0 -4
  42. data/spec/lib/message_bus/multi_process_spec.rb +6 -7
  43. data/spec/lib/message_bus/rack/middleware_spec.rb +47 -43
  44. data/spec/lib/message_bus/timer_thread_spec.rb +0 -2
  45. data/spec/lib/message_bus_spec.rb +59 -43
  46. data/spec/spec_helper.rb +16 -4
  47. metadata +12 -9
  48. data/spec/lib/message_bus/backends/postgres_spec.rb +0 -221
  49. data/spec/lib/message_bus/backends/redis_spec.rb +0 -271
@@ -1,7 +1,6 @@
1
1
  require 'message_bus'
2
2
 
3
- #MessageBus.long_polling_interval = 1000 * 2
3
+ # MessageBus.long_polling_interval = 1000 * 2
4
4
 
5
5
  use MessageBus::Rack::Middleware
6
- run lambda { |env| [200, {"Content-Type" => "text/html"}, ["Howdy"]] }
7
-
6
+ run lambda { |_env| [200, { "Content-Type" => "text/html" }, ["Howdy"]] }
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "monitor"
3
4
  require "set"
4
5
  require "message_bus/version"
@@ -15,13 +16,16 @@ if defined?(::Rails)
15
16
  require 'message_bus/rails/railtie'
16
17
  end
17
18
 
19
+ # @see MessageBus::Implementation
18
20
  module MessageBus; end
19
21
  MessageBus::BACKENDS = {}
20
22
  class MessageBus::InvalidMessage < StandardError; end
21
23
  class MessageBus::BusDestroyed < StandardError; end
22
24
 
25
+ # The main server-side interface to a message bus for the purposes of
26
+ # configuration, publishing and subscribing
23
27
  module MessageBus::Implementation
24
- # Configuration options hash
28
+ # @return [Hash<Symbol => Object>] Configuration options hash
25
29
  attr_reader :config
26
30
 
27
31
  # Like Mutex but safe for recursive calls
@@ -34,10 +38,13 @@ module MessageBus::Implementation
34
38
  @mutex = Synchronizer.new
35
39
  end
36
40
 
41
+ # @param [Boolean] val whether or not to cache static assets for the diagnostics pages
42
+ # @return [void]
37
43
  def cache_assets=(val)
38
44
  configure(cache_assets: val)
39
45
  end
40
46
 
47
+ # @return [Boolean] whether or not to cache static assets for the diagnostics pages
41
48
  def cache_assets
42
49
  if defined? @config[:cache_assets]
43
50
  @config[:cache_assets]
@@ -46,12 +53,17 @@ module MessageBus::Implementation
46
53
  end
47
54
  end
48
55
 
56
+ # @param [Logger] logger a logger object to be used by the bus
57
+ # @return [void]
49
58
  def logger=(logger)
50
59
  configure(logger: logger)
51
60
  end
52
61
 
62
+ # @return [Logger] the logger used by the bus. If not explicitly set,
63
+ # is configured to log to STDOUT at INFO level.
53
64
  def logger
54
65
  return @config[:logger] if @config[:logger]
66
+
55
67
  require 'logger'
56
68
  logger = Logger.new(STDOUT)
57
69
  logger.level = Logger::INFO
@@ -59,32 +71,47 @@ module MessageBus::Implementation
59
71
  logger
60
72
  end
61
73
 
74
+ # @return [Boolean] whether or not chunked encoding is enabled. If not
75
+ # explicitly set, defaults to true.
62
76
  def chunked_encoding_enabled?
63
77
  @config[:chunked_encoding_enabled] == false ? false : true
64
78
  end
65
79
 
80
+ # @param [Boolean] val whether or not to enable chunked encoding
81
+ # @return [void]
66
82
  def chunked_encoding_enabled=(val)
67
83
  configure(chunked_encoding_enabled: val)
68
84
  end
69
85
 
86
+ # @return [Boolean] whether or not long polling is enabled. If not explicitly
87
+ # set, defaults to true.
70
88
  def long_polling_enabled?
71
89
  @config[:long_polling_enabled] == false ? false : true
72
90
  end
73
91
 
92
+ # @param [Boolean] val whether or not to enable long polling
93
+ # @return [void]
74
94
  def long_polling_enabled=(val)
75
95
  configure(long_polling_enabled: val)
76
96
  end
77
97
 
78
- # The number of simultanuous clients we can service
79
- # will revert to polling if we are out of slots
98
+ # @param [Integer] val The number of simultanuous clients we can service;
99
+ # will revert to polling if we are out of slots
100
+ # @return [void]
80
101
  def max_active_clients=(val)
81
102
  configure(max_active_clients: val)
82
103
  end
83
104
 
105
+ # @return [Integer] The number of simultanuous clients we can service;
106
+ # will revert to polling if we are out of slots. Defaults to 1000 if not
107
+ # explicitly set.
84
108
  def max_active_clients
85
109
  @config[:max_active_clients] || 1000
86
110
  end
87
111
 
112
+ # @return [Boolean] whether or not Rack Hijack is enabled. If not explicitly
113
+ # set, will default to true, unless we're on Passenger without the ability
114
+ # to set the advertised_concurrency_level to 0.
88
115
  def rack_hijack_enabled?
89
116
  if @config[:rack_hijack_enabled].nil?
90
117
  enable = true
@@ -104,62 +131,107 @@ module MessageBus::Implementation
104
131
  @config[:rack_hijack_enabled]
105
132
  end
106
133
 
134
+ # @param [Boolean] val whether or not to enable Rack Hijack
135
+ # @return [void]
107
136
  def rack_hijack_enabled=(val)
108
137
  configure(rack_hijack_enabled: val)
109
138
  end
110
139
 
140
+ # @param [Integer] millisecs the long-polling interval in milliseconds
141
+ # @return [void]
111
142
  def long_polling_interval=(millisecs)
112
143
  configure(long_polling_interval: millisecs)
113
144
  end
114
145
 
146
+ # @return [Integer] the long-polling interval in milliseconds. If not
147
+ # explicitly set, defaults to 25,000.
115
148
  def long_polling_interval
116
149
  @config[:long_polling_interval] || 25 * 1000
117
150
  end
118
151
 
152
+ # @return [Boolean] whether the bus is disabled or not
153
+ def off?
154
+ @off
155
+ end
156
+
157
+ # Disables publication to the bus
158
+ # @return [void]
119
159
  def off
120
160
  @off = true
121
161
  end
122
162
 
163
+ # Enables publication to the bus
164
+ # @return [void]
123
165
  def on
124
- @off = false
166
+ @destroyed = @off = false
125
167
  end
126
168
 
169
+ # Overrides existing configuration
170
+ # @param [Hash<Symbol => Object>] config values to merge into existing config
171
+ # @return [void]
127
172
  def configure(config)
128
173
  @config.merge!(config)
129
174
  end
130
175
 
131
- # Allow us to inject a redis db
176
+ # Overrides existing configuration, explicitly enabling the redis backend
177
+ # @param [Hash<Symbol => Object>] config values to merge into existing config
178
+ # @return [void]
132
179
  def redis_config=(config)
133
180
  configure(config.merge(backend: :redis))
134
181
  end
135
182
 
136
183
  alias redis_config config
137
184
 
185
+ # @yield [env] a routine to determine the site ID for a subscriber
186
+ # @yieldparam [optional, Rack::Request::Env] env the subscriber request environment
187
+ # @yieldreturn [optional, String] the site ID for the subscriber
188
+ # @return [void]
138
189
  def site_id_lookup(&blk)
139
190
  configure(site_id_lookup: blk) if blk
140
191
  @config[:site_id_lookup]
141
192
  end
142
193
 
194
+ # @yield [env] a routine to determine the user ID for a subscriber (authenticate)
195
+ # @yieldparam [optional, Rack::Request::Env] env the subscriber request environment
196
+ # @yieldreturn [optional, String, Integer] the user ID for the subscriber
197
+ # @return [void]
143
198
  def user_id_lookup(&blk)
144
199
  configure(user_id_lookup: blk) if blk
145
200
  @config[:user_id_lookup]
146
201
  end
147
202
 
203
+ # @yield [env] a routine to determine the group IDs for a subscriber
204
+ # @yieldparam [optional, Rack::Request::Env] env the subscriber request environment
205
+ # @yieldreturn [optional, Array<String,Integer>] the group IDs for the subscriber
206
+ # @return [void]
148
207
  def group_ids_lookup(&blk)
149
208
  configure(group_ids_lookup: blk) if blk
150
209
  @config[:group_ids_lookup]
151
210
  end
152
211
 
212
+ # @yield [env] a routine to determine if a request comes from an admin user
213
+ # @yieldparam [Rack::Request::Env] env the subscriber request environment
214
+ # @yieldreturn [Boolean] whether or not the request is from an admin user
215
+ # @return [void]
153
216
  def is_admin_lookup(&blk)
154
217
  configure(is_admin_lookup: blk) if blk
155
218
  @config[:is_admin_lookup]
156
219
  end
157
220
 
221
+ # @yield [env, e] a routine to handle exceptions raised when handling a subscriber request
222
+ # @yieldparam [Rack::Request::Env] env the subscriber request environment
223
+ # @yieldparam [Exception] e the exception that was raised
224
+ # @yieldreturn [optional, Array<(Integer,Hash,Array)>] a Rack response to be delivered
225
+ # @return [void]
158
226
  def on_middleware_error(&blk)
159
227
  configure(on_middleware_error: blk) if blk
160
228
  @config[:on_middleware_error]
161
229
  end
162
230
 
231
+ # @yield [env] a routine to determine extra headers to be set on a subscriber response
232
+ # @yieldparam [Rack::Request::Env] env the subscriber request environment
233
+ # @yieldreturn [Hash<String => String>] the extra headers to set on the response
234
+ # @return [void]
163
235
  def extra_response_headers_lookup(&blk)
164
236
  configure(extra_response_headers_lookup: blk) if blk
165
237
  @config[:extra_response_headers_lookup]
@@ -175,10 +247,14 @@ module MessageBus::Implementation
175
247
  @config[:on_disconnect]
176
248
  end
177
249
 
250
+ # @param [Boolean] val whether or not to allow broadcasting (debugging)
251
+ # @return [void]
178
252
  def allow_broadcast=(val)
179
253
  configure(allow_broadcast: val)
180
254
  end
181
255
 
256
+ # @return [Boolean] whether or not broadcasting is allowed. If not explicitly
257
+ # set, defaults to false unless we're in Rails test or development mode.
182
258
  def allow_broadcast?
183
259
  @config[:allow_broadcast] ||=
184
260
  if defined? ::Rails
@@ -188,10 +264,14 @@ module MessageBus::Implementation
188
264
  end
189
265
  end
190
266
 
267
+ # @param [MessageBus::Backend::Base] pub_sub a configured backend
268
+ # @return [void]
191
269
  def reliable_pub_sub=(pub_sub)
192
270
  configure(reliable_pub_sub: pub_sub)
193
271
  end
194
272
 
273
+ # @return [MessageBus::Backend::Base] the configured backend. If not
274
+ # explicitly set, will be loaded based on the configuration provided.
195
275
  def reliable_pub_sub
196
276
  @mutex.synchronize do
197
277
  return nil if @destroyed
@@ -208,16 +288,37 @@ module MessageBus::Implementation
208
288
  end
209
289
  end
210
290
 
291
+ # @return [Symbol] the name of the backend implementation configured
211
292
  def backend
212
293
  @config[:backend] || :redis
213
294
  end
214
295
 
296
+ # Enables diagnostics tracking
297
+ # @return [void]
215
298
  def enable_diagnostics
216
299
  MessageBus::Diagnostics.enable(self)
217
300
  end
218
301
 
302
+ # Publishes a message to a channel
303
+ #
304
+ # @param [String] channel the name of the channel to which the message should be published
305
+ # @param [JSON] data some data to publish to the channel. Must be an object that can be encoded as JSON
306
+ # @param [Hash] opts
307
+ # @option opts [Array<String>] :client_ids (`nil`) the unique client IDs to which the message should be available. If nil, available to all.
308
+ # @option opts [Array<String,Integer>] :user_ids (`nil`) the user IDs to which the message should be available. If nil, available to all.
309
+ # @option opts [Array<String,Integer>] :group_ids (`nil`) the group IDs to which the message should be available. If nil, available to all.
310
+ # @option opts [String] :site_id (`nil`) the site ID to scope the message to; used for hosting multiple
311
+ # applications or instances of an application against a single message_bus
312
+ # @option opts [nil,Integer] :max_backlog_age the longest amount of time a message may live in a backlog before beging removed, in seconds
313
+ # @option opts [nil,Integer] :max_backlog_size the largest permitted size (number of messages) for the channel backlog; beyond this capacity, old messages will be dropped
314
+ #
315
+ # @return [Integer] the channel-specific ID the message was given
316
+ #
317
+ # @raise [MessageBus::BusDestroyed] if the bus is destroyed
318
+ # @raise [MessageBus::InvalidMessage] if attempting to put permission restrictions on a globally-published message
219
319
  def publish(channel, data, opts = nil)
220
320
  return if @off
321
+
221
322
  @mutex.synchronize do
222
323
  raise ::MessageBus::BusDestroyed if @destroyed
223
324
  end
@@ -256,6 +357,17 @@ module MessageBus::Implementation
256
357
  reliable_pub_sub.publish(encoded_channel_name, encoded_data, channel_opts)
257
358
  end
258
359
 
360
+ # Subscribe to messages. Each message will be delivered by yielding to the
361
+ # passed block as soon as it is available. This will block until subscription
362
+ # is terminated.
363
+ #
364
+ # @param [String,nil] channel the name of the channel to which we should
365
+ # subscribe. If `nil`, messages on every channel will be provided.
366
+ #
367
+ # @yield [message] a message-handler block
368
+ # @yieldparam [MessageBus::Message] message each message as it is delivered
369
+ #
370
+ # @return [void]
259
371
  def blocking_subscribe(channel = nil, &blk)
260
372
  if channel
261
373
  reliable_pub_sub.subscribe(encode_channel_name(channel), &blk)
@@ -264,45 +376,78 @@ module MessageBus::Implementation
264
376
  end
265
377
  end
266
378
 
267
- ENCODE_SITE_TOKEN = "$|$"
268
-
269
- # encode channel name to include site
270
- def encode_channel_name(channel, site_id = nil)
271
- if (site_id || site_id_lookup) && !global?(channel)
272
- raise ArgumentError.new channel if channel.include? ENCODE_SITE_TOKEN
273
- "#{channel}#{ENCODE_SITE_TOKEN}#{site_id || site_id_lookup.call}"
274
- else
275
- channel
276
- end
277
- end
278
-
279
- def decode_channel_name(channel)
280
- channel.split(ENCODE_SITE_TOKEN)
281
- end
282
-
379
+ # Subscribe to messages on a particular channel. Each message since the
380
+ # last ID specified will be delivered by yielding to the passed block as
381
+ # soon as it is available. This will not block, but instead the callbacks
382
+ # will be executed asynchronously in a dedicated subscriber thread.
383
+ #
384
+ # @param [String] channel the name of the channel to which we should subscribe
385
+ # @param [#to_i] last_id the channel-specific ID of the last message that the caller received on the specified channel
386
+ #
387
+ # @yield [message] a message-handler block
388
+ # @yieldparam [MessageBus::Message] message each message as it is delivered
389
+ #
390
+ # @return [Proc] the callback block that will be executed
283
391
  def subscribe(channel = nil, last_id = -1, &blk)
284
392
  subscribe_impl(channel, nil, last_id, &blk)
285
393
  end
286
394
 
287
- # subscribe only on current site
395
+ # Subscribe to messages on a particular channel, filtered by the current site
396
+ # (@see #site_id_lookup). Each message since the last ID specified will be
397
+ # delivered by yielding to the passed block as soon as it is available. This
398
+ # will not block, but instead the callbacks will be executed asynchronously
399
+ # in a dedicated subscriber thread.
400
+ #
401
+ # @param [String] channel the name of the channel to which we should subscribe
402
+ # @param [#to_i] last_id the channel-specific ID of the last message that the caller received on the specified channel
403
+ #
404
+ # @yield [message] a message-handler block
405
+ # @yieldparam [MessageBus::Message] message each message as it is delivered
406
+ #
407
+ # @return [Proc] the callback block that will be executed
288
408
  def local_subscribe(channel = nil, last_id = -1, &blk)
289
- site_id = site_id_lookup.call if site_id_lookup && ! global?(channel)
409
+ site_id = site_id_lookup.call if site_id_lookup && !global?(channel)
290
410
  subscribe_impl(channel, site_id, last_id, &blk)
291
411
  end
292
412
 
413
+ # Removes a subscription to a particular channel.
414
+ #
415
+ # @param [String] channel the name of the channel from which we should unsubscribe
416
+ # @param [Proc,nil] blk the callback which should be removed. If `nil`, removes all.
417
+ #
418
+ # @return [void]
293
419
  def unsubscribe(channel = nil, &blk)
294
420
  unsubscribe_impl(channel, nil, &blk)
295
421
  end
296
422
 
423
+ # Removes a subscription to a particular channel, filtered by the current site
424
+ # (@see #site_id_lookup).
425
+ #
426
+ # @param [String] channel the name of the channel from which we should unsubscribe
427
+ # @param [Proc,nil] blk the callback which should be removed. If `nil`, removes all.
428
+ #
429
+ # @return [void]
297
430
  def local_unsubscribe(channel = nil, &blk)
298
431
  site_id = site_id_lookup.call if site_id_lookup
299
432
  unsubscribe_impl(channel, site_id, &blk)
300
433
  end
301
434
 
435
+ # Get messages from the global backlog since the last ID specified
436
+ #
437
+ # @param [#to_i] last_id the global ID of the last message that the caller received
438
+ #
439
+ # @return [Array<MessageBus::Message>] all messages published on any channel since the specified last ID
302
440
  def global_backlog(last_id = nil)
303
441
  backlog(nil, last_id)
304
442
  end
305
443
 
444
+ # Get messages from a channel backlog since the last ID specified, filtered by site
445
+ #
446
+ # @param [String] channel the name of the channel in question
447
+ # @param [#to_i] last_id the channel-specific ID of the last message that the caller received on the specified channel
448
+ # @param [String] site_id the ID of the site by which to filter
449
+ #
450
+ # @return [Array<MessageBus::Message>] all messages published to the specified channel since the specified last ID
306
451
  def backlog(channel = nil, last_id = nil, site_id = nil)
307
452
  old =
308
453
  if channel
@@ -317,10 +462,21 @@ module MessageBus::Implementation
317
462
  old
318
463
  end
319
464
 
320
- def last_id(channel , site_id = nil)
465
+ # Get the ID of the last message published on a channel, filtered by site
466
+ #
467
+ # @param [String] channel the name of the channel in question
468
+ # @param [String] site_id the ID of the site by which to filter
469
+ #
470
+ # @return [Integer] the channel-specific ID of the last message published to the given channel
471
+ def last_id(channel, site_id = nil)
321
472
  reliable_pub_sub.last_id(encode_channel_name(channel, site_id))
322
473
  end
323
474
 
475
+ # Get the last message published on a channel
476
+ #
477
+ # @param [String] channel the name of the channel in question
478
+ #
479
+ # @return [MessageBus::Message] the last message published to the given channel
324
480
  def last_message(channel)
325
481
  if last_id = last_id(channel)
326
482
  messages = backlog(channel, last_id - 1)
@@ -330,12 +486,17 @@ module MessageBus::Implementation
330
486
  end
331
487
  end
332
488
 
333
- # mostly used in tests to detroy entire bus
489
+ # Stops listening for publications and stops executing scheduled tasks.
490
+ # Mostly used in tests to detroy entire bus.
491
+ # @return [void]
334
492
  def destroy
335
493
  return if @destroyed
494
+
336
495
  reliable_pub_sub.global_unsubscribe
337
496
 
338
497
  @mutex.synchronize do
498
+ return if @destroyed
499
+
339
500
  @subscriptions ||= {}
340
501
  @destroyed = true
341
502
  end
@@ -343,6 +504,11 @@ module MessageBus::Implementation
343
504
  timer.stop
344
505
  end
345
506
 
507
+ # Performs routines that are necessary after a process fork, typically
508
+ # triggered by a forking webserver. Performs whatever the backend requires
509
+ # and ensures the server is listening for publications and running
510
+ # scheduled tasks.
511
+ # @return [void]
346
512
  def after_fork
347
513
  reliable_pub_sub.after_fork
348
514
  ensure_subscriber_thread
@@ -350,17 +516,22 @@ module MessageBus::Implementation
350
516
  timer.queue {}
351
517
  end
352
518
 
519
+ # @return [Boolean] whether or not the server is actively listening for
520
+ # publications on the bus
353
521
  def listening?
354
522
  @subscriber_thread && @subscriber_thread.alive?
355
523
  end
356
524
 
357
- # will reset all keys
525
+ # (see MessageBus::Backend::Base#reset!)
358
526
  def reset!
359
- reliable_pub_sub.reset!
527
+ reliable_pub_sub.reset! if reliable_pub_sub
360
528
  end
361
529
 
530
+ # @return [MessageBus::TimerThread] the timer thread used for triggering
531
+ # scheduled routines at specific times/intervals.
362
532
  def timer
363
533
  return @timer_thread if @timer_thread
534
+
364
535
  @timer_thread ||= begin
365
536
  t = MessageBus::TimerThread.new
366
537
  t.on_error do |e|
@@ -370,18 +541,37 @@ module MessageBus::Implementation
370
541
  end
371
542
  end
372
543
 
373
- # set to 0 to disable, anything higher and
374
- # a keepalive will run every N seconds, if it fails
375
- # process is killed
544
+ # @param [Integer] interval the keepalive interval in seconds.
545
+ # Set to 0 to disable; anything higher and a keepalive will run every N
546
+ # seconds. If it fails, the process is killed.
376
547
  def keepalive_interval=(interval)
377
548
  configure(keepalive_interval: interval)
378
549
  end
379
550
 
551
+ # @return [Integer] the keepalive interval in seconds. If not explicitly set,
552
+ # defaults to `60`.
380
553
  def keepalive_interval
381
554
  @config[:keepalive_interval] || 60
382
555
  end
383
556
 
384
- protected
557
+ private
558
+
559
+ ENCODE_SITE_TOKEN = "$|$"
560
+
561
+ # encode channel name to include site
562
+ def encode_channel_name(channel, site_id = nil)
563
+ if (site_id || site_id_lookup) && !global?(channel)
564
+ raise ArgumentError.new channel if channel.include? ENCODE_SITE_TOKEN
565
+
566
+ "#{channel}#{ENCODE_SITE_TOKEN}#{site_id || site_id_lookup.call}"
567
+ else
568
+ channel
569
+ end
570
+ end
571
+
572
+ def decode_channel_name(channel)
573
+ channel.split(ENCODE_SITE_TOKEN)
574
+ end
385
575
 
386
576
  def global?(channel)
387
577
  channel && channel.start_with?('/global/'.freeze)
@@ -410,7 +600,6 @@ module MessageBus::Implementation
410
600
  end
411
601
 
412
602
  def subscribe_impl(channel, site_id, last_id, &blk)
413
-
414
603
  raise MessageBus::BusDestroyed if @destroyed
415
604
 
416
605
  if last_id >= 0
@@ -450,11 +639,11 @@ module MessageBus::Implementation
450
639
  end
451
640
 
452
641
  raise MessageBus::BusDestroyed if @destroyed
642
+
453
643
  blk
454
644
  end
455
645
 
456
646
  def unsubscribe_impl(channel, site_id, &blk)
457
-
458
647
  @mutex.synchronize do
459
648
  if blk
460
649
  @subscriptions[site_id][channel].delete blk
@@ -462,12 +651,12 @@ module MessageBus::Implementation
462
651
  @subscriptions[site_id][channel] = []
463
652
  end
464
653
  end
465
-
466
654
  end
467
655
 
468
656
  def ensure_subscriber_thread
469
657
  @mutex.synchronize do
470
658
  return if (@subscriber_thread && @subscriber_thread.alive?) || @destroyed
659
+
471
660
  @subscriber_thread = new_subscriber_thread
472
661
  end
473
662
  end
@@ -475,7 +664,6 @@ module MessageBus::Implementation
475
664
  MIN_KEEPALIVE = 20
476
665
 
477
666
  def new_subscriber_thread
478
-
479
667
  thread = Thread.new do
480
668
  begin
481
669
  global_subscribe_thread unless @destroyed
@@ -569,7 +757,6 @@ module MessageBus::Implementation
569
757
  a.each(&block) if a
570
758
  end
571
759
  end
572
-
573
760
  end
574
761
 
575
762
  module MessageBus
@@ -578,6 +765,7 @@ module MessageBus
578
765
  end
579
766
 
580
767
  # allows for multiple buses per app
768
+ # @see MessageBus::Implementation
581
769
  class MessageBus::Instance
582
770
  include MessageBus::Implementation
583
771
  end