message_bus 3.3.4 → 3.3.8

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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.js +21 -0
  3. data/.github/workflows/ci.yml +99 -0
  4. data/.gitignore +2 -0
  5. data/.prettierrc +1 -0
  6. data/.rubocop.yml +3 -1
  7. data/CHANGELOG +30 -8
  8. data/DEV.md +7 -0
  9. data/Gemfile +0 -25
  10. data/LICENSE +1 -1
  11. data/README.md +34 -15
  12. data/Rakefile +13 -8
  13. data/assets/message-bus-ajax.js +4 -10
  14. data/assets/message-bus.js +69 -76
  15. data/bench/codecs/all_codecs.rb +39 -0
  16. data/bench/codecs/marshal.rb +11 -0
  17. data/bench/codecs/packed_string.rb +67 -0
  18. data/bench/codecs/string_hack.rb +47 -0
  19. data/bench/codecs_large_user_list.rb +29 -0
  20. data/bench/codecs_standard_message.rb +29 -0
  21. data/examples/bench/bench.lua +2 -2
  22. data/lib/message_bus/backends/base.rb +8 -5
  23. data/lib/message_bus/backends/memory.rb +6 -2
  24. data/lib/message_bus/backends/postgres.rb +27 -18
  25. data/lib/message_bus/backends/redis.rb +9 -6
  26. data/lib/message_bus/client.rb +6 -7
  27. data/lib/message_bus/codec/base.rb +18 -0
  28. data/lib/message_bus/codec/json.rb +15 -0
  29. data/lib/message_bus/codec/oj.rb +21 -0
  30. data/lib/message_bus/connection_manager.rb +1 -1
  31. data/lib/message_bus/distributed_cache.rb +3 -1
  32. data/lib/message_bus/http_client.rb +2 -2
  33. data/lib/message_bus/rack/diagnostics.rb +30 -8
  34. data/lib/message_bus/rack/middleware.rb +22 -16
  35. data/lib/message_bus/rack/thin_ext.rb +2 -1
  36. data/lib/message_bus/version.rb +1 -1
  37. data/lib/message_bus.rb +42 -22
  38. data/message_bus.gemspec +21 -3
  39. data/package-lock.json +3744 -0
  40. data/package.json +15 -8
  41. data/spec/assets/SpecHelper.js +6 -5
  42. data/spec/assets/message-bus.spec.js +9 -6
  43. data/spec/helpers.rb +23 -7
  44. data/spec/integration/http_client_spec.rb +1 -1
  45. data/spec/lib/fake_async_middleware.rb +1 -0
  46. data/spec/lib/message_bus/backend_spec.rb +13 -44
  47. data/spec/lib/message_bus/client_spec.rb +7 -6
  48. data/spec/lib/message_bus/connection_manager_spec.rb +4 -0
  49. data/spec/lib/message_bus/distributed_cache_spec.rb +5 -7
  50. data/spec/lib/message_bus/multi_process_spec.rb +19 -9
  51. data/spec/lib/message_bus/rack/middleware_spec.rb +18 -6
  52. data/spec/lib/message_bus_spec.rb +13 -8
  53. data/spec/spec_helper.rb +8 -9
  54. data/spec/support/jasmine-browser.json +16 -0
  55. data/vendor/assets/javascripts/message-bus-ajax.js +4 -10
  56. data/vendor/assets/javascripts/message-bus.js +69 -76
  57. metadata +231 -11
  58. data/.travis.yml +0 -17
  59. data/lib/message_bus/em_ext.rb +0 -6
  60. data/spec/assets/support/jasmine.yml +0 -126
  61. data/spec/assets/support/jasmine_helper.rb +0 -11
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "message_bus/backends/base"
4
-
5
3
  module MessageBus
6
4
  module Backends
7
5
  # The memory backend stores published messages in a simple array per
@@ -214,6 +212,12 @@ module MessageBus
214
212
  client.reset!
215
213
  end
216
214
 
215
+ # No-op; this backend doesn't maintain any storage connections.
216
+ # (see Base#destroy)
217
+ def destroy
218
+ nil
219
+ end
220
+
217
221
  # (see Base#expire_all_backlogs!)
218
222
  def expire_all_backlogs!
219
223
  client.expire_all_backlogs!
@@ -2,8 +2,6 @@
2
2
 
3
3
  require 'pg'
4
4
 
5
- require "message_bus/backends/base"
6
-
7
5
  module MessageBus
8
6
  module Backends
9
7
  # The Postgres backend stores published messages in a single Postgres table
@@ -46,6 +44,7 @@ module MessageBus
46
44
  @listening_on = {}
47
45
  @available = []
48
46
  @allocated = {}
47
+ @subscribe_connection = nil
49
48
  @mutex = Mutex.new
50
49
  @pid = Process.pid
51
50
  end
@@ -87,10 +86,12 @@ module MessageBus
87
86
  hold { |conn| exec_prepared(conn, 'get_message', [channel, id]) { |r| r.getvalue(0, 0) } }
88
87
  end
89
88
 
90
- def reconnect
89
+ def after_fork
91
90
  sync do
92
- @listening_on.clear
91
+ @pid = Process.pid
92
+ INHERITED_CONNECTIONS.concat(@available)
93
93
  @available.clear
94
+ @listening_on.clear
94
95
  end
95
96
  end
96
97
 
@@ -102,6 +103,13 @@ module MessageBus
102
103
  end
103
104
  end
104
105
 
106
+ def destroy
107
+ sync do
108
+ @available.each(&:close)
109
+ @available.clear
110
+ end
111
+ end
112
+
105
113
  # use with extreme care, will nuke all of the data
106
114
  def expire_all_backlogs!
107
115
  reset!
@@ -133,7 +141,7 @@ module MessageBus
133
141
  listener = Listener.new
134
142
  yield listener
135
143
 
136
- conn = raw_pg_connection
144
+ conn = @subscribe_connection = raw_pg_connection
137
145
  conn.exec "LISTEN #{channel}"
138
146
  listener.do_sub.call
139
147
  while listening_on?(channel, obj)
@@ -147,6 +155,9 @@ module MessageBus
147
155
 
148
156
  conn.exec "UNLISTEN #{channel}"
149
157
  nil
158
+ ensure
159
+ @subscribe_connection&.close
160
+ @subscribe_connection = nil
150
161
  end
151
162
 
152
163
  def unsubscribe
@@ -172,11 +183,7 @@ module MessageBus
172
183
  def hold
173
184
  current_pid = Process.pid
174
185
  if current_pid != @pid
175
- @pid = current_pid
176
- sync do
177
- INHERITED_CONNECTIONS.concat(@available)
178
- @available.clear
179
- end
186
+ after_fork
180
187
  end
181
188
 
182
189
  if conn = sync { @allocated[Thread.current] }
@@ -251,12 +258,13 @@ module MessageBus
251
258
  # after 7 days inactive backlogs will be removed
252
259
  @max_backlog_age = 604800
253
260
  @clear_every = config[:clear_every] || 1
261
+ @mutex = Mutex.new
254
262
  end
255
263
 
256
- # Reconnects to Postgres; used after a process fork, typically triggerd by a forking webserver
264
+ # Reconnects to Postgres; used after a process fork, typically triggered by a forking webserver
257
265
  # @see Base#after_fork
258
266
  def after_fork
259
- client.reconnect
267
+ client.after_fork
260
268
  end
261
269
 
262
270
  # (see Base#reset!)
@@ -264,6 +272,11 @@ module MessageBus
264
272
  client.reset!
265
273
  end
266
274
 
275
+ # (see Base#destroy)
276
+ def destroy
277
+ client.destroy
278
+ end
279
+
267
280
  # (see Base#expire_all_backlogs!)
268
281
  def expire_all_backlogs!
269
282
  client.expire_all_backlogs!
@@ -279,7 +292,7 @@ module MessageBus
279
292
  msg = MessageBus::Message.new backlog_id, backlog_id, channel, data
280
293
  payload = msg.encode
281
294
  c.publish postgresql_channel_name, payload
282
- if backlog_id % clear_every == 0
295
+ if backlog_id && backlog_id % clear_every == 0
283
296
  max_backlog_size = (opts && opts[:max_backlog_size]) || self.max_backlog_size
284
297
  max_backlog_age = (opts && opts[:max_backlog_age]) || self.max_backlog_age
285
298
  c.clear_global_backlog(backlog_id, @max_global_backlog_size)
@@ -399,11 +412,7 @@ module MessageBus
399
412
  private
400
413
 
401
414
  def client
402
- @client ||= new_connection
403
- end
404
-
405
- def new_connection
406
- Client.new(@config)
415
+ @client || @mutex.synchronize { @client ||= Client.new(@config) }
407
416
  end
408
417
 
409
418
  def postgresql_channel_name
@@ -3,8 +3,6 @@
3
3
  require 'redis'
4
4
  require 'digest'
5
5
 
6
- require "message_bus/backends/base"
7
-
8
6
  module MessageBus
9
7
  module Backends
10
8
  # The Redis backend stores published messages in Redis sorted sets (using
@@ -64,10 +62,10 @@ module MessageBus
64
62
  @max_backlog_age = 604800
65
63
  end
66
64
 
67
- # Reconnects to Redis; used after a process fork, typically triggerd by a forking webserver
65
+ # Reconnects to Redis; used after a process fork, typically triggered by a forking webserver
68
66
  # @see Base#after_fork
69
67
  def after_fork
70
- pub_redis.disconnect!
68
+ @pub_redis&.disconnect!
71
69
  end
72
70
 
73
71
  # (see Base#reset!)
@@ -77,6 +75,11 @@ module MessageBus
77
75
  end
78
76
  end
79
77
 
78
+ # (see Base#destroy)
79
+ def destroy
80
+ @pub_redis&.disconnect!
81
+ end
82
+
80
83
  # Deletes all backlogs and their data. Does not delete ID pointers, so new publications will get IDs that continue from the last publication before the expiry. Use with extreme caution.
81
84
  # @see Base#expire_all_backlogs!
82
85
  def expire_all_backlogs!
@@ -104,8 +107,8 @@ module MessageBus
104
107
 
105
108
  local global_id = redis.call("INCR", global_id_key)
106
109
  local backlog_id = redis.call("INCR", backlog_id_key)
107
- local payload = string.format("%i|%i|%s", global_id, backlog_id, start_payload)
108
- local global_backlog_message = string.format("%i|%s", backlog_id, channel)
110
+ local payload = table.concat({ global_id, backlog_id, start_payload }, "|")
111
+ local global_backlog_message = table.concat({ backlog_id, channel }, "|")
109
112
 
110
113
  redis.call("ZADD", backlog_key, backlog_id, payload)
111
114
  redis.call("EXPIRE", backlog_key, max_backlog_age)
@@ -46,6 +46,9 @@ class MessageBus::Client
46
46
  @bus = opts[:message_bus] || MessageBus
47
47
  @subscriptions = {}
48
48
  @chunks_sent = 0
49
+ @async_response = nil
50
+ @io = nil
51
+ @wrote_headers = false
49
52
  end
50
53
 
51
54
  # @yield executed with a lock on the Client instance
@@ -220,7 +223,7 @@ class MessageBus::Client
220
223
 
221
224
  private
222
225
 
223
- # heavily optimised to avoid all uneeded allocations
226
+ # heavily optimised to avoid all unneeded allocations
224
227
  NEWLINE = "\r\n".freeze
225
228
  COLON_SPACE = ": ".freeze
226
229
  HTTP_11 = "HTTP/1.1 200 OK\r\n".freeze
@@ -261,7 +264,7 @@ class MessageBus::Client
261
264
  @wrote_headers = true
262
265
  end
263
266
 
264
- # chunked encoding may be "re-chunked" by proxies, so add a seperator
267
+ # chunked encoding may be "re-chunked" by proxies, so add a separator
265
268
  postfix = NEWLINE + "|" + NEWLINE
266
269
  data = data.gsub(postfix, NEWLINE + "||" + NEWLINE)
267
270
  chunk_length = data.bytesize + postfix.bytesize
@@ -275,11 +278,7 @@ class MessageBus::Client
275
278
  @async_response << postfix
276
279
  @async_response << NEWLINE
277
280
  else
278
- @io.write(chunk_length.to_s(16))
279
- @io.write(NEWLINE)
280
- @io.write(data)
281
- @io.write(postfix)
282
- @io.write(NEWLINE)
281
+ @io.write(chunk_length.to_s(16) << NEWLINE << data << postfix << NEWLINE)
283
282
  end
284
283
  end
285
284
 
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MessageBus
4
+ module Codec
5
+ class Base
6
+ def encode(hash)
7
+ raise ConcreteClassMustImplementError
8
+ end
9
+
10
+ def decode(payload)
11
+ raise ConcreteClassMustImplementError
12
+ end
13
+ end
14
+
15
+ autoload :Json, File.expand_path("json", __dir__)
16
+ autoload :Oj, File.expand_path("oj", __dir__)
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MessageBus
4
+ module Codec
5
+ class Json < Base
6
+ def encode(hash)
7
+ JSON.dump(hash)
8
+ end
9
+
10
+ def decode(payload)
11
+ JSON.parse(payload)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'oj' unless defined? ::Oj
4
+
5
+ module MessageBus
6
+ module Codec
7
+ class Oj < Base
8
+ def initialize(options = { mode: :compat })
9
+ @options = options
10
+ end
11
+
12
+ def encode(hash)
13
+ ::Oj.dump(hash, @options)
14
+ end
15
+
16
+ def decode(payload)
17
+ ::Oj.load(payload, @options)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -37,7 +37,7 @@ class MessageBus::ConnectionManager
37
37
  rescue
38
38
  # pipe may be broken, move on
39
39
  end
40
- # turns out you can delete from a set while itereating
40
+ # turns out you can delete from a set while iterating
41
41
  remove_client(client) if client.closed?
42
42
  end
43
43
  end
@@ -22,6 +22,7 @@ module MessageBus
22
22
  @lock = Mutex.new
23
23
  @message_bus = message_bus || MessageBus
24
24
  @publish_queue_in_memory = publish_queue_in_memory
25
+ @app_version = nil
25
26
  end
26
27
 
27
28
  def subscribers
@@ -45,7 +46,8 @@ module MessageBus
45
46
  hash = current.hash(message.site_id || DEFAULT_SITE_ID)
46
47
 
47
48
  case payload["op"]
48
- when "set" then hash[payload["key"]] = payload["marshalled"] ? Marshal.load(Base64.decode64(payload["value"])) : payload["value"]
49
+ # TODO: consider custom marshal support with a restricted set
50
+ when "set" then hash[payload["key"]] = payload["marshalled"] ? Marshal.load(Base64.decode64(payload["value"])) : payload["value"] # rubocop:disable Security/MarshalLoad
49
51
  when "delete" then hash.delete(payload["key"])
50
52
  when "clear" then hash.clear
51
53
  end
@@ -146,8 +146,8 @@ module MessageBus
146
146
  #
147
147
  # A last_message_id may be provided.
148
148
  # * -1 will subscribe to all new messages
149
- # * -2 will recieve last message + all new messages
150
- # * -3 will recieve last 2 message + all new messages
149
+ # * -2 will receive last message + all new messages
150
+ # * -3 will receive last 2 message + all new messages
151
151
  #
152
152
  # @example Subscribing to a channel with `last_message_id`
153
153
  # client.subscribe("/test", last_message_id: -2) do |payload|
@@ -13,6 +13,15 @@ class MessageBus::Rack::Diagnostics
13
13
  @bus = config[:message_bus] || MessageBus
14
14
  end
15
15
 
16
+ JS_ASSETS = %w{
17
+ jquery-1.8.2.js
18
+ react.js
19
+ react-dom.js
20
+ babel.min.js
21
+ message-bus.js
22
+ application.jsx
23
+ }
24
+
16
25
  # Process an HTTP request from a subscriber client
17
26
  # @param [Rack::Request::Env] env the request environment
18
27
  def call(env)
@@ -39,9 +48,9 @@ class MessageBus::Rack::Diagnostics
39
48
  end
40
49
 
41
50
  asset = route.split('/assets/')[1]
42
- if asset && !asset !~ /\//
51
+
52
+ if asset && JS_ASSETS.include?(asset)
43
53
  content = asset_contents(asset)
44
- split = asset.split('.')
45
54
  return [200, { 'Content-Type' => 'application/javascript;charset=UTF-8' }, [content]]
46
55
  end
47
56
 
@@ -75,6 +84,23 @@ class MessageBus::Rack::Diagnostics
75
84
  File.expand_path("../../../../assets/#{asset}", __FILE__)
76
85
  end
77
86
 
87
+ def script_tags
88
+ tags = []
89
+
90
+ JS_ASSETS.each do |asset|
91
+ type =
92
+ if asset.end_with?('.js')
93
+ 'text/javascript'
94
+ elsif asset.end_with?('.jsx')
95
+ 'text/jsx'
96
+ end
97
+
98
+ tags << js_asset(asset, type)
99
+ end
100
+
101
+ tags.join("\n")
102
+ end
103
+
78
104
  def index
79
105
  html = <<~HTML
80
106
  <!DOCTYPE html>
@@ -83,12 +109,8 @@ class MessageBus::Rack::Diagnostics
83
109
  </head>
84
110
  <body>
85
111
  <div id="app"></div>
86
- #{js_asset "jquery-1.8.2.js"}
87
- #{js_asset "react.js"}
88
- #{js_asset "react-dom.js"}
89
- #{js_asset "babel.min.js"}
90
- #{js_asset "message-bus.js"}
91
- #{js_asset "application.jsx", "text/jsx"}
112
+
113
+ #{script_tags}
92
114
  </body>
93
115
  </html>
94
116
  HTML
@@ -66,6 +66,12 @@ class MessageBus::Rack::Middleware
66
66
  private
67
67
 
68
68
  def handle_request(env)
69
+ # Prevent simple polling from clobbering the session
70
+ # See: https://github.com/discourse/message_bus/issues/257
71
+ if (rack_session_options = env[Rack::RACK_SESSION_OPTIONS])
72
+ rack_session_options[:skip] = true
73
+ end
74
+
69
75
  # special debug/test route
70
76
  if @bus.allow_broadcast? && env['PATH_INFO'] == @broadcast_route
71
77
  parsed = Rack::Request.new(env)
@@ -81,6 +87,22 @@ class MessageBus::Rack::Middleware
81
87
  client_id = env['PATH_INFO'][@base_route_length..-1].split("/")[0]
82
88
  return [404, {}, ["not found"]] unless client_id
83
89
 
90
+ headers = {}
91
+ headers["Cache-Control"] = "must-revalidate, private, max-age=0"
92
+ headers["Content-Type"] = "application/json; charset=utf-8"
93
+ headers["Pragma"] = "no-cache"
94
+ headers["Expires"] = "0"
95
+
96
+ if @bus.extra_response_headers_lookup
97
+ @bus.extra_response_headers_lookup.call(env).each do |k, v|
98
+ headers[k] = v
99
+ end
100
+ end
101
+
102
+ if env["REQUEST_METHOD"] == "OPTIONS"
103
+ return [200, headers, ["OK"]]
104
+ end
105
+
84
106
  user_id = @bus.user_id_lookup.call(env) if @bus.user_id_lookup
85
107
  group_ids = @bus.group_ids_lookup.call(env) if @bus.group_ids_lookup
86
108
  site_id = @bus.site_id_lookup.call(env) if @bus.site_id_lookup
@@ -111,22 +133,6 @@ class MessageBus::Rack::Middleware
111
133
  end
112
134
  end
113
135
 
114
- headers = {}
115
- headers["Cache-Control"] = "must-revalidate, private, max-age=0"
116
- headers["Content-Type"] = "application/json; charset=utf-8"
117
- headers["Pragma"] = "no-cache"
118
- headers["Expires"] = "0"
119
-
120
- if @bus.extra_response_headers_lookup
121
- @bus.extra_response_headers_lookup.call(env).each do |k, v|
122
- headers[k] = v
123
- end
124
- end
125
-
126
- if env["REQUEST_METHOD"] == "OPTIONS"
127
- return [200, headers, ["OK"]]
128
- end
129
-
130
136
  long_polling = @bus.long_polling_enabled? &&
131
137
  env['QUERY_STRING'] !~ /dlp=t/ &&
132
138
  @connection_manager.client_count < @bus.max_active_clients
@@ -9,6 +9,7 @@ module Thin
9
9
 
10
10
  def initialize
11
11
  @queue = []
12
+ @body_callback = nil
12
13
  end
13
14
 
14
15
  def call(body)
@@ -38,7 +39,7 @@ module Thin
38
39
  end
39
40
  end
40
41
 
41
- # Response whos body is sent asynchronously.
42
+ # Response which body is sent asynchronously.
42
43
  class AsyncResponse
43
44
  include Rack::Response::Helpers
44
45
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MessageBus
4
- VERSION = "3.3.4"
4
+ VERSION = "3.3.8"
5
5
  end
data/lib/message_bus.rb CHANGED
@@ -2,18 +2,22 @@
2
2
 
3
3
  require "monitor"
4
4
  require "set"
5
- require "message_bus/version"
6
- require "message_bus/message"
7
- require "message_bus/client"
8
- require "message_bus/connection_manager"
9
- require "message_bus/diagnostics"
10
- require "message_bus/rack/middleware"
11
- require "message_bus/rack/diagnostics"
12
- require "message_bus/timer_thread"
5
+
6
+ require_relative "message_bus/version"
7
+ require_relative "message_bus/message"
8
+ require_relative "message_bus/client"
9
+ require_relative "message_bus/connection_manager"
10
+ require_relative "message_bus/diagnostics"
11
+ require_relative "message_bus/rack/middleware"
12
+ require_relative "message_bus/rack/diagnostics"
13
+ require_relative "message_bus/timer_thread"
14
+ require_relative "message_bus/codec/base"
15
+ require_relative "message_bus/backends"
16
+ require_relative "message_bus/backends/base"
13
17
 
14
18
  # we still need to take care of the logger
15
- if defined?(::Rails)
16
- require 'message_bus/rails/railtie'
19
+ if defined?(::Rails::Engine)
20
+ require_relative 'message_bus/rails/railtie'
17
21
  end
18
22
 
19
23
  # @see MessageBus::Implementation
@@ -37,6 +41,10 @@ module MessageBus::Implementation
37
41
  def initialize
38
42
  @config = {}
39
43
  @mutex = Synchronizer.new
44
+ @off = false
45
+ @destroyed = false
46
+ @timer_thread = nil
47
+ @subscriber_thread = nil
40
48
  end
41
49
 
42
50
  # @param [Boolean] val whether or not to cache static assets for the diagnostics pages
@@ -96,14 +104,14 @@ module MessageBus::Implementation
96
104
  configure(long_polling_enabled: val)
97
105
  end
98
106
 
99
- # @param [Integer] val The number of simultanuous clients we can service;
107
+ # @param [Integer] val The number of simultaneous clients we can service;
100
108
  # will revert to polling if we are out of slots
101
109
  # @return [void]
102
110
  def max_active_clients=(val)
103
111
  configure(max_active_clients: val)
104
112
  end
105
113
 
106
- # @return [Integer] The number of simultanuous clients we can service;
114
+ # @return [Integer] The number of simultaneous clients we can service;
107
115
  # will revert to polling if we are out of slots. Defaults to 1000 if not
108
116
  # explicitly set.
109
117
  def max_active_clients
@@ -271,13 +279,24 @@ module MessageBus::Implementation
271
279
  # set, defaults to false unless we're in Rails test or development mode.
272
280
  def allow_broadcast?
273
281
  @config[:allow_broadcast] ||=
274
- if defined? ::Rails
282
+ if defined? ::Rails.env
275
283
  ::Rails.env.test? || ::Rails.env.development?
276
284
  else
277
285
  false
278
286
  end
279
287
  end
280
288
 
289
+ # @param [MessageBus::Codec::Base] codec used to encode and decode Message payloads
290
+ # @return [void]
291
+ def transport_codec=(codec)
292
+ configure(transport_codec: codec)
293
+ end
294
+
295
+ # @return [MessageBus::Codec::Base] codec used to encode and decode Message payloads
296
+ def transport_codec
297
+ @config[:transport_codec] ||= MessageBus::Codec::Json.new
298
+ end
299
+
281
300
  # @param [MessageBus::Backend::Base] pub_sub a configured backend
282
301
  # @return [void]
283
302
  def reliable_pub_sub=(pub_sub)
@@ -323,7 +342,7 @@ module MessageBus::Implementation
323
342
  # @option opts [Array<String,Integer>] :group_ids (`nil`) the group IDs to which the message should be available. If nil, available to all.
324
343
  # @option opts [String] :site_id (`nil`) the site ID to scope the message to; used for hosting multiple
325
344
  # applications or instances of an application against a single message_bus
326
- # @option opts [nil,Integer] :max_backlog_age the longest amount of time a message may live in a backlog before beging removed, in seconds
345
+ # @option opts [nil,Integer] :max_backlog_age the longest amount of time a message may live in a backlog before being removed, in seconds
327
346
  # @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
328
347
  #
329
348
  # @return [Integer] the channel-specific ID the message was given
@@ -358,12 +377,12 @@ module MessageBus::Implementation
358
377
  raise ::MessageBus::InvalidMessageTarget
359
378
  end
360
379
 
361
- encoded_data = JSON.dump(
362
- data: data,
363
- user_ids: user_ids,
364
- group_ids: group_ids,
365
- client_ids: client_ids
366
- )
380
+ encoded_data = transport_codec.encode({
381
+ "data" => data,
382
+ "user_ids" => user_ids,
383
+ "group_ids" => group_ids,
384
+ "client_ids" => client_ids
385
+ })
367
386
 
368
387
  channel_opts = {}
369
388
 
@@ -512,12 +531,13 @@ module MessageBus::Implementation
512
531
  end
513
532
 
514
533
  # Stops listening for publications and stops executing scheduled tasks.
515
- # Mostly used in tests to detroy entire bus.
534
+ # Mostly used in tests to destroy entire bus.
516
535
  # @return [void]
517
536
  def destroy
518
537
  return if @destroyed
519
538
 
520
539
  reliable_pub_sub.global_unsubscribe
540
+ reliable_pub_sub.destroy
521
541
 
522
542
  @mutex.synchronize do
523
543
  return if @destroyed
@@ -626,7 +646,7 @@ module MessageBus::Implementation
626
646
  channel, site_id = decode_channel_name(msg.channel)
627
647
  msg.channel = channel
628
648
  msg.site_id = site_id
629
- parsed = JSON.parse(msg.data)
649
+ parsed = transport_codec.decode(msg.data)
630
650
  msg.data = parsed["data"]
631
651
  msg.user_ids = parsed["user_ids"]
632
652
  msg.group_ids = parsed["group_ids"]
data/message_bus.gemspec CHANGED
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # -*- encoding: utf-8 -*-
3
2
 
4
3
  require File.expand_path('../lib/message_bus/version', __FILE__)
5
4
 
@@ -8,7 +7,7 @@ Gem::Specification.new do |gem|
8
7
  gem.email = ["sam.saffron@gmail.com"]
9
8
  gem.description = %q{A message bus for rack}
10
9
  gem.summary = %q{}
11
- gem.homepage = "https://github.com/SamSaffron/message_bus"
10
+ gem.homepage = "https://github.com/discourse/message_bus"
12
11
  gem.license = "MIT"
13
12
  gem.files = `git ls-files`.split($\) +
14
13
  ["vendor/assets/javascripts/message-bus.js", "vendor/assets/javascripts/message-bus-ajax.js"]
@@ -17,8 +16,27 @@ Gem::Specification.new do |gem|
17
16
  gem.name = "message_bus"
18
17
  gem.require_paths = ["lib"]
19
18
  gem.version = MessageBus::VERSION
20
- gem.required_ruby_version = ">= 2.3.0"
19
+ gem.required_ruby_version = ">= 2.6.0"
20
+
21
21
  gem.add_runtime_dependency 'rack', '>= 1.1.3'
22
+
23
+ # Optional runtime dependencies
22
24
  gem.add_development_dependency 'redis'
23
25
  gem.add_development_dependency 'pg'
26
+ gem.add_development_dependency 'concurrent-ruby' # for distributed-cache
27
+
28
+ gem.add_development_dependency 'minitest'
29
+ gem.add_development_dependency 'minitest-hooks'
30
+ gem.add_development_dependency 'minitest-global_expectations'
31
+ gem.add_development_dependency 'rake'
32
+ gem.add_development_dependency 'http_parser.rb'
33
+ gem.add_development_dependency 'thin'
34
+ gem.add_development_dependency 'rack-test'
35
+ gem.add_development_dependency 'puma'
36
+ gem.add_development_dependency 'm'
37
+ gem.add_development_dependency 'byebug'
38
+ gem.add_development_dependency 'oj'
39
+ gem.add_development_dependency 'yard'
40
+ gem.add_development_dependency 'rubocop-discourse'
41
+ gem.add_development_dependency 'rubocop-rspec'
24
42
  end