message_bus 2.0.2 → 2.0.3
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 +4 -4
- data/.rubocop.yml +104 -0
- data/CHANGELOG +9 -0
- data/README.md +62 -11
- data/lib/message_bus.rb +25 -25
- data/lib/message_bus/backends/memory.rb +14 -13
- data/lib/message_bus/backends/postgres.rb +34 -29
- data/lib/message_bus/backends/redis.rb +13 -15
- data/lib/message_bus/client.rb +21 -11
- data/lib/message_bus/connection_manager.rb +4 -3
- data/lib/message_bus/message.rb +7 -5
- data/lib/message_bus/rack/diagnostics.rb +5 -4
- data/lib/message_bus/rack/middleware.rb +19 -18
- data/lib/message_bus/rack/thin_ext.rb +2 -1
- data/lib/message_bus/rails/railtie.rb +16 -3
- data/lib/message_bus/timer_thread.rb +18 -14
- data/lib/message_bus/version.rb +2 -1
- data/message_bus.gemspec +2 -1
- data/spec/lib/message_bus/backends/postgres_spec.rb +1 -1
- data/spec/lib/message_bus/backends/redis_spec.rb +1 -1
- data/spec/lib/message_bus/client_spec.rb +24 -1
- data/spec/lib/message_bus/connection_manager_spec.rb +4 -4
- data/spec/lib/message_bus/rack/middleware_spec.rb +10 -2
- data/spec/lib/message_bus_spec.rb +1 -1
- data/vendor/assets/javascripts/message-bus-ajax.js +1 -0
- data/vendor/assets/javascripts/message-bus.js +1 -0
- metadata +14 -13
- data/vendor/assets/javascripts/message-bus-ajax.js +0 -44
- data/vendor/assets/javascripts/message-bus.js +0 -431
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'json'
|
2
3
|
|
3
4
|
# our little message bus, accepts long polling and polling
|
@@ -48,10 +49,10 @@ class MessageBus::Rack::Middleware
|
|
48
49
|
def self.backlog_to_json(backlog)
|
49
50
|
m = backlog.map do |msg|
|
50
51
|
{
|
51
|
-
:
|
52
|
-
:
|
53
|
-
:
|
54
|
-
:
|
52
|
+
global_id: msg.global_id,
|
53
|
+
message_id: msg.message_id,
|
54
|
+
channel: msg.channel,
|
55
|
+
data: msg.data
|
55
56
|
}
|
56
57
|
end.to_a
|
57
58
|
JSON.dump(m)
|
@@ -62,13 +63,13 @@ class MessageBus::Rack::Middleware
|
|
62
63
|
return @app.call(env) unless env['PATH_INFO'] =~ /^\/message-bus\//
|
63
64
|
|
64
65
|
# special debug/test route
|
65
|
-
if @bus.allow_broadcast? && env['PATH_INFO'] == '/message-bus/broadcast'
|
66
|
-
|
67
|
-
|
68
|
-
|
66
|
+
if @bus.allow_broadcast? && env['PATH_INFO'] == '/message-bus/broadcast'
|
67
|
+
parsed = Rack::Request.new(env)
|
68
|
+
@bus.publish parsed["channel"], parsed["data"]
|
69
|
+
return [200, { "Content-Type" => "text/html" }, ["sent"]]
|
69
70
|
end
|
70
71
|
|
71
|
-
if env['PATH_INFO'].start_with? '/message-bus/_diagnostics'
|
72
|
+
if env['PATH_INFO'].start_with? '/message-bus/_diagnostics'
|
72
73
|
diags = MessageBus::Rack::Diagnostics.new(@app, message_bus: @bus)
|
73
74
|
return diags.call(env)
|
74
75
|
end
|
@@ -97,8 +98,8 @@ class MessageBus::Rack::Middleware
|
|
97
98
|
request = Rack::Request.new(env)
|
98
99
|
is_json = request.content_type && request.content_type.include?('application/json')
|
99
100
|
data = is_json ? JSON.parse(request.body.read) : request.POST
|
100
|
-
data.each do |k,v|
|
101
|
-
if k == "__seq"
|
101
|
+
data.each do |k, v|
|
102
|
+
if k == "__seq"
|
102
103
|
client.seq = v.to_i
|
103
104
|
else
|
104
105
|
client.subscribe(k, v)
|
@@ -113,7 +114,7 @@ class MessageBus::Rack::Middleware
|
|
113
114
|
headers["Expires"] = "0"
|
114
115
|
|
115
116
|
if @bus.extra_response_headers_lookup
|
116
|
-
@bus.extra_response_headers_lookup.call(env).each do |k,v|
|
117
|
+
@bus.extra_response_headers_lookup.call(env).each do |k, v|
|
117
118
|
headers[k] = v
|
118
119
|
end
|
119
120
|
end
|
@@ -123,11 +124,11 @@ class MessageBus::Rack::Middleware
|
|
123
124
|
end
|
124
125
|
|
125
126
|
long_polling = @bus.long_polling_enabled? &&
|
126
|
-
env['QUERY_STRING'] !~ /dlp=t
|
127
|
+
env['QUERY_STRING'] !~ /dlp=t/ &&
|
127
128
|
@connection_manager.client_count < @bus.max_active_clients
|
128
129
|
|
129
|
-
allow_chunked = env['HTTP_VERSION'
|
130
|
-
allow_chunked &&= !env['HTTP_DONT_CHUNK'
|
130
|
+
allow_chunked = env['HTTP_VERSION'] == 'HTTP/1.1'
|
131
|
+
allow_chunked &&= !env['HTTP_DONT_CHUNK']
|
131
132
|
allow_chunked &&= @bus.chunked_encoding_enabled?
|
132
133
|
|
133
134
|
client.use_chunked = allow_chunked
|
@@ -159,7 +160,7 @@ class MessageBus::Rack::Middleware
|
|
159
160
|
response = Thin::AsyncResponse.new(env)
|
160
161
|
end
|
161
162
|
|
162
|
-
headers.each do |k,v|
|
163
|
+
headers.each do |k, v|
|
163
164
|
response.headers[k] = v
|
164
165
|
end
|
165
166
|
|
@@ -183,7 +184,7 @@ class MessageBus::Rack::Middleware
|
|
183
184
|
end
|
184
185
|
|
185
186
|
rescue => e
|
186
|
-
if @bus.on_middleware_error && result
|
187
|
+
if @bus.on_middleware_error && result = @bus.on_middleware_error.call(env, e)
|
187
188
|
result
|
188
189
|
else
|
189
190
|
raise
|
@@ -203,7 +204,7 @@ class MessageBus::Rack::Middleware
|
|
203
204
|
def add_client_with_timeout(client)
|
204
205
|
@connection_manager.add_client(client)
|
205
206
|
|
206
|
-
client.cleanup_timer = MessageBus.timer.queue(
|
207
|
+
client.cleanup_timer = MessageBus.timer.queue(@bus.long_polling_interval.to_f / 1000) {
|
207
208
|
begin
|
208
209
|
client.cleanup_timer = nil
|
209
210
|
client.ensure_closed!
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
# there is also another in cramp this is from https://github.com/macournoyer/thin_async/blob/master/lib/thin/async.rb
|
2
3
|
module Thin
|
3
4
|
unless defined?(DeferrableBody)
|
@@ -40,7 +41,7 @@ module Thin
|
|
40
41
|
attr_reader :headers, :callback, :closed
|
41
42
|
attr_accessor :status
|
42
43
|
|
43
|
-
def initialize(env, status=200, headers={})
|
44
|
+
def initialize(env, status = 200, headers = {})
|
44
45
|
@callback = env['async.callback']
|
45
46
|
@body = DeferrableBody.new
|
46
47
|
@status = status
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module MessageBus; module Rails; end; end
|
2
3
|
|
3
4
|
# rails engine for asset pipeline
|
@@ -11,12 +12,24 @@ class MessageBus::Rails::Railtie < ::Rails::Railtie
|
|
11
12
|
#
|
12
13
|
# To handle either case, we insert it before ActionDispatch::Flash.
|
13
14
|
#
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
# For APIs or apps that have ActionDispatch::Flash deleted from the middleware
|
16
|
+
# stack we just push MessageBus to the bottom.
|
17
|
+
if api_only?(app.config) || flash_middleware_deleted?(app.middleware)
|
17
18
|
app.middleware.use(MessageBus::Rack::Middleware)
|
19
|
+
else
|
20
|
+
app.middleware.insert_before(ActionDispatch::Flash, MessageBus::Rack::Middleware)
|
18
21
|
end
|
19
22
|
|
20
23
|
MessageBus.logger = Rails.logger
|
21
24
|
end
|
25
|
+
|
26
|
+
def api_only?(config)
|
27
|
+
return false unless config.respond_to?(:api_only)
|
28
|
+
config.api_only
|
29
|
+
end
|
30
|
+
|
31
|
+
def flash_middleware_deleted?(middleware)
|
32
|
+
ops = middleware.instance_variable_get(:@operations)
|
33
|
+
ops.any? { |m| m[0] == :delete && m[1].include?(ActionDispatch::Flash) }
|
34
|
+
end
|
22
35
|
end
|
@@ -1,9 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
class MessageBus::TimerThread
|
2
3
|
|
3
4
|
attr_reader :jobs
|
4
5
|
|
5
6
|
class Cancelable
|
6
|
-
NOOP = proc{}
|
7
|
+
NOOP = proc {}
|
7
8
|
|
8
9
|
def initialize(job)
|
9
10
|
@job = job
|
@@ -26,8 +27,8 @@ class MessageBus::TimerThread
|
|
26
27
|
@jobs = []
|
27
28
|
@mutex = Mutex.new
|
28
29
|
@next = nil
|
29
|
-
@thread = Thread.new{do_work}
|
30
|
-
@on_error = lambda{|e| STDERR.puts "Exception while processing Timer:\n #{e.backtrace.join("\n")}"}
|
30
|
+
@thread = Thread.new { do_work }
|
31
|
+
@on_error = lambda { |e| STDERR.puts "Exception while processing Timer:\n #{e.backtrace.join("\n")}" }
|
31
32
|
end
|
32
33
|
|
33
34
|
def stop
|
@@ -51,12 +52,12 @@ class MessageBus::TimerThread
|
|
51
52
|
result.current = queue(delay, &do_work)
|
52
53
|
end
|
53
54
|
end
|
54
|
-
result.current = queue(delay
|
55
|
+
result.current = queue(delay, &do_work)
|
55
56
|
result
|
56
57
|
end
|
57
58
|
|
58
59
|
# queue a block to run after a certain delay (in seconds)
|
59
|
-
def queue(delay=0, &block)
|
60
|
+
def queue(delay = 0, &block)
|
60
61
|
queue_time = Time.new.to_f + delay
|
61
62
|
job = [queue_time, block]
|
62
63
|
|
@@ -64,20 +65,23 @@ class MessageBus::TimerThread
|
|
64
65
|
i = @jobs.length
|
65
66
|
while i > 0
|
66
67
|
i -= 1
|
67
|
-
current,_ = @jobs[i]
|
68
|
-
|
68
|
+
current, _ = @jobs[i]
|
69
|
+
if current < queue_time
|
70
|
+
i += 1
|
71
|
+
break
|
72
|
+
end
|
69
73
|
end
|
70
74
|
@jobs.insert(i, job)
|
71
|
-
@next = queue_time if i==0
|
75
|
+
@next = queue_time if i == 0
|
72
76
|
end
|
73
77
|
|
74
78
|
unless @thread.alive?
|
75
79
|
@mutex.synchronize do
|
76
|
-
@thread = Thread.new{do_work} unless @thread.alive?
|
80
|
+
@thread = Thread.new { do_work } unless @thread.alive?
|
77
81
|
end
|
78
82
|
end
|
79
83
|
|
80
|
-
if @thread.status == "sleep"
|
84
|
+
if @thread.status == "sleep"
|
81
85
|
@thread.wakeup
|
82
86
|
end
|
83
87
|
|
@@ -93,22 +97,22 @@ class MessageBus::TimerThread
|
|
93
97
|
def do_work
|
94
98
|
while !@stopped
|
95
99
|
if @next && @next <= Time.new.to_f
|
96
|
-
_,blk = @mutex.synchronize { @jobs.shift }
|
100
|
+
_, blk = @mutex.synchronize { @jobs.shift }
|
97
101
|
begin
|
98
102
|
blk.call
|
99
103
|
rescue => e
|
100
104
|
@on_error.call(e) if @on_error
|
101
105
|
end
|
102
106
|
@mutex.synchronize do
|
103
|
-
@next,_ = @jobs[0]
|
107
|
+
@next, _ = @jobs[0]
|
104
108
|
end
|
105
109
|
end
|
106
110
|
unless @next && @next <= Time.new.to_f
|
107
111
|
sleep_time = 1000
|
108
112
|
@mutex.synchronize do
|
109
|
-
sleep_time = @next-Time.new.to_f if @next
|
113
|
+
sleep_time = @next - Time.new.to_f if @next
|
110
114
|
end
|
111
|
-
sleep [0,sleep_time].max
|
115
|
+
sleep [0, sleep_time].max
|
112
116
|
end
|
113
117
|
end
|
114
118
|
end
|
data/lib/message_bus/version.rb
CHANGED
data/message_bus.gemspec
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
# -*- encoding: utf-8 -*-
|
2
3
|
require File.expand_path('../lib/message_bus/version', __FILE__)
|
3
4
|
|
@@ -10,7 +11,7 @@ Gem::Specification.new do |gem|
|
|
10
11
|
gem.license = "MIT"
|
11
12
|
|
12
13
|
gem.files = `git ls-files`.split($\)
|
13
|
-
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
14
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
14
15
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
15
16
|
gem.name = "message_bus"
|
16
17
|
gem.require_paths = ["lib"]
|
@@ -5,10 +5,14 @@ describe MessageBus::Client do
|
|
5
5
|
|
6
6
|
describe "subscriptions" do
|
7
7
|
|
8
|
+
def setup_client(client_id)
|
9
|
+
MessageBus::Client.new client_id: client_id, message_bus: @bus
|
10
|
+
end
|
11
|
+
|
8
12
|
before do
|
9
13
|
@bus = MessageBus::Instance.new
|
10
14
|
@bus.configure(MESSAGE_BUS_CONFIG)
|
11
|
-
@client =
|
15
|
+
@client = setup_client('abc')
|
12
16
|
end
|
13
17
|
|
14
18
|
after do
|
@@ -119,6 +123,25 @@ describe MessageBus::Client do
|
|
119
123
|
log[0].data.must_equal("/hello" => 0)
|
120
124
|
end
|
121
125
|
|
126
|
+
it 'provides status updates to clients that are not allowed to a message' do
|
127
|
+
another_client = setup_client('def')
|
128
|
+
clients = [@client, another_client]
|
129
|
+
|
130
|
+
clients.each { |client| client.subscribe('/hello', nil) }
|
131
|
+
|
132
|
+
@bus.publish("/hello", "world", client_ids: ['abc'])
|
133
|
+
|
134
|
+
log = @client.backlog
|
135
|
+
log.length.must_equal 1
|
136
|
+
log[0].channel.must_equal '/hello'
|
137
|
+
log[0].data.must_equal 'world'
|
138
|
+
|
139
|
+
log = another_client.backlog
|
140
|
+
log.length.must_equal 1
|
141
|
+
log[0].channel.must_equal '/__status'
|
142
|
+
log[0].data.must_equal({ '/hello' => 1 })
|
143
|
+
end
|
144
|
+
|
122
145
|
it "should provide a list of subscriptions" do
|
123
146
|
@client.subscribe('/hello', nil)
|
124
147
|
@client.subscriptions['/hello'].wont_equal nil
|
@@ -53,7 +53,7 @@ describe MessageBus::ConnectionManager do
|
|
53
53
|
m = MessageBus::Message.new(1,1,"test","data")
|
54
54
|
m.site_id = 9
|
55
55
|
@manager.notify_clients(m)
|
56
|
-
@resp.sent
|
56
|
+
assert_nil @resp.sent
|
57
57
|
end
|
58
58
|
|
59
59
|
it "should notify clients on the correct site" do
|
@@ -69,8 +69,8 @@ describe MessageBus::ConnectionManager do
|
|
69
69
|
m.site_id = 10
|
70
70
|
@manager.notify_clients(m)
|
71
71
|
parsed = JSON.parse(@resp.sent)
|
72
|
-
parsed[0]["site_id"]
|
73
|
-
parsed[0]["user_id"]
|
72
|
+
assert_nil parsed[0]["site_id"]
|
73
|
+
assert_nil parsed[0]["user_id"]
|
74
74
|
end
|
75
75
|
|
76
76
|
it "should not deliver unselected" do
|
@@ -78,7 +78,7 @@ describe MessageBus::ConnectionManager do
|
|
78
78
|
m.user_ids = [5]
|
79
79
|
m.site_id = 10
|
80
80
|
@manager.notify_clients(m)
|
81
|
-
@resp.sent
|
81
|
+
assert_nil @resp.sent
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
@@ -260,7 +260,12 @@ describe MessageBus::Rack::Middleware do
|
|
260
260
|
}
|
261
261
|
|
262
262
|
parsed = JSON.parse(last_response.body)
|
263
|
-
parsed.length.must_equal
|
263
|
+
parsed.length.must_equal 1
|
264
|
+
|
265
|
+
message = parsed.first
|
266
|
+
|
267
|
+
message["channel"].must_equal "/__status"
|
268
|
+
message["data"].must_equal("/foo" => 1)
|
264
269
|
|
265
270
|
@bus.user_id_lookup do |env|
|
266
271
|
1
|
@@ -286,7 +291,10 @@ describe MessageBus::Rack::Middleware do
|
|
286
291
|
}
|
287
292
|
|
288
293
|
parsed = JSON.parse(last_response.body)
|
289
|
-
parsed.
|
294
|
+
message = parsed.first
|
295
|
+
|
296
|
+
message["channel"].must_equal "/__status"
|
297
|
+
message["data"].must_equal("/foo" => 1)
|
290
298
|
|
291
299
|
@bus.group_ids_lookup do |env|
|
292
300
|
[1,7,4,100]
|
@@ -159,7 +159,7 @@ describe MessageBus do
|
|
159
159
|
@bus.publish("/bob", "dylan")
|
160
160
|
@bus.publish("/bob", "marley")
|
161
161
|
@bus.last_message("/bob").data.must_equal "marley"
|
162
|
-
@bus.last_message("/nothing")
|
162
|
+
assert_nil @bus.last_message("/nothing")
|
163
163
|
end
|
164
164
|
|
165
165
|
describe "global subscriptions" do
|
@@ -0,0 +1 @@
|
|
1
|
+
../../../assets/message-bus-ajax.js
|
@@ -0,0 +1 @@
|
|
1
|
+
../../../assets/message-bus.js
|
metadata
CHANGED
@@ -1,55 +1,55 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: message_bus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-08-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 1.1.3
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 1.1.3
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: redis
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: pg
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
description: A message bus for rack
|
@@ -59,8 +59,9 @@ executables: []
|
|
59
59
|
extensions: []
|
60
60
|
extra_rdoc_files: []
|
61
61
|
files:
|
62
|
-
- .gitignore
|
63
|
-
- .
|
62
|
+
- ".gitignore"
|
63
|
+
- ".rubocop.yml"
|
64
|
+
- ".travis.yml"
|
64
65
|
- CHANGELOG
|
65
66
|
- Gemfile
|
66
67
|
- Guardfile
|
@@ -130,17 +131,17 @@ require_paths:
|
|
130
131
|
- lib
|
131
132
|
required_ruby_version: !ruby/object:Gem::Requirement
|
132
133
|
requirements:
|
133
|
-
- -
|
134
|
+
- - ">="
|
134
135
|
- !ruby/object:Gem::Version
|
135
136
|
version: 1.9.3
|
136
137
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
137
138
|
requirements:
|
138
|
-
- -
|
139
|
+
- - ">="
|
139
140
|
- !ruby/object:Gem::Version
|
140
141
|
version: '0'
|
141
142
|
requirements: []
|
142
143
|
rubyforge_project:
|
143
|
-
rubygems_version: 2.
|
144
|
+
rubygems_version: 2.5.2
|
144
145
|
signing_key:
|
145
146
|
specification_version: 4
|
146
147
|
summary: ''
|