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.
- checksums.yaml +4 -4
- data/.rubocop.yml +13 -92
- data/.rubocop_todo.yml +659 -0
- data/.travis.yml +1 -1
- data/CHANGELOG +61 -0
- data/Dockerfile +18 -0
- data/Gemfile +3 -1
- data/Guardfile +0 -1
- data/README.md +188 -101
- data/Rakefile +12 -1
- data/assets/message-bus.js +1 -1
- data/docker-compose.yml +46 -0
- data/examples/bench/config.ru +8 -9
- data/examples/bench/unicorn.conf.rb +1 -1
- data/examples/chat/chat.rb +150 -153
- data/examples/minimal/config.ru +2 -3
- data/lib/message_bus.rb +224 -36
- data/lib/message_bus/backends.rb +7 -0
- data/lib/message_bus/backends/base.rb +184 -0
- data/lib/message_bus/backends/memory.rb +304 -226
- data/lib/message_bus/backends/postgres.rb +359 -318
- data/lib/message_bus/backends/redis.rb +380 -337
- data/lib/message_bus/client.rb +99 -41
- data/lib/message_bus/connection_manager.rb +29 -21
- data/lib/message_bus/diagnostics.rb +50 -41
- data/lib/message_bus/distributed_cache.rb +5 -7
- data/lib/message_bus/message.rb +2 -2
- data/lib/message_bus/rack/diagnostics.rb +65 -55
- data/lib/message_bus/rack/middleware.rb +64 -44
- data/lib/message_bus/rack/thin_ext.rb +13 -9
- data/lib/message_bus/rails/railtie.rb +2 -0
- data/lib/message_bus/timer_thread.rb +2 -2
- data/lib/message_bus/version.rb +2 -1
- data/message_bus.gemspec +3 -2
- data/spec/assets/support/jasmine_helper.rb +1 -1
- data/spec/lib/fake_async_middleware.rb +1 -6
- data/spec/lib/message_bus/assets/asset_encoding_spec.rb +3 -3
- data/spec/lib/message_bus/backend_spec.rb +409 -0
- data/spec/lib/message_bus/client_spec.rb +8 -11
- data/spec/lib/message_bus/connection_manager_spec.rb +8 -14
- data/spec/lib/message_bus/distributed_cache_spec.rb +0 -4
- data/spec/lib/message_bus/multi_process_spec.rb +6 -7
- data/spec/lib/message_bus/rack/middleware_spec.rb +47 -43
- data/spec/lib/message_bus/timer_thread_spec.rb +0 -2
- data/spec/lib/message_bus_spec.rb +59 -43
- data/spec/spec_helper.rb +16 -4
- metadata +12 -9
- data/spec/lib/message_bus/backends/postgres_spec.rb +0 -221
- data/spec/lib/message_bus/backends/redis_spec.rb +0 -271
data/Rakefile
CHANGED
@@ -8,6 +8,17 @@ require 'jasmine'
|
|
8
8
|
ENV['JASMINE_CONFIG_PATH'] ||= File.join(Dir.pwd, 'spec', 'assets', 'support', 'jasmine.yml')
|
9
9
|
load 'jasmine/tasks/jasmine.rake'
|
10
10
|
|
11
|
+
require 'rubocop/rake_task'
|
12
|
+
RuboCop::RakeTask.new
|
13
|
+
|
14
|
+
require 'yard'
|
15
|
+
YARD::Rake::YardocTask.new
|
16
|
+
|
17
|
+
desc "Generate documentation for Yard, and fail if there are any warnings"
|
18
|
+
task :test_doc do
|
19
|
+
sh "yard --fail-on-warning #{'--no-progress' if ENV['CI']}"
|
20
|
+
end
|
21
|
+
|
11
22
|
Bundler.require(:default, :test)
|
12
23
|
|
13
24
|
task default: :spec
|
@@ -34,7 +45,7 @@ run_spec = proc do |backend|
|
|
34
45
|
end
|
35
46
|
end
|
36
47
|
|
37
|
-
task spec: [:spec_redis, :spec_postgres, :
|
48
|
+
task spec: [:spec_memory, :spec_redis, :spec_postgres, :spec_client_js, :rubocop, :test_doc]
|
38
49
|
|
39
50
|
task spec_client_js: 'jasmine:ci'
|
40
51
|
|
data/assets/message-bus.js
CHANGED
@@ -440,7 +440,7 @@
|
|
440
440
|
|
441
441
|
// Subscribe to a channel
|
442
442
|
// if lastId is 0 or larger, it will recieve messages AFTER that id
|
443
|
-
// if lastId is
|
443
|
+
// if lastId is negative it will perform lookbehind
|
444
444
|
// -1 will subscribe to all new messages
|
445
445
|
// -2 will recieve last message + all new messages
|
446
446
|
// -3 will recieve last 2 messages + all new messages
|
data/docker-compose.yml
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
version: '3'
|
2
|
+
volumes:
|
3
|
+
bundle_config:
|
4
|
+
bundle:
|
5
|
+
redis_data:
|
6
|
+
postgres_data:
|
7
|
+
services:
|
8
|
+
tests:
|
9
|
+
build:
|
10
|
+
context: .
|
11
|
+
environment:
|
12
|
+
BUNDLE_TO: /usr/local/bundle
|
13
|
+
REDISURL: redis://redis:6379
|
14
|
+
PGHOST: postgres
|
15
|
+
PGUSER: postgres
|
16
|
+
PGPASSWORD: "1234"
|
17
|
+
volumes:
|
18
|
+
- .:/usr/src/app
|
19
|
+
- bundle_config:/home/src/app/.bundle
|
20
|
+
- bundle:/usr/local/bundle
|
21
|
+
depends_on:
|
22
|
+
- postgres
|
23
|
+
- redis
|
24
|
+
docs:
|
25
|
+
build:
|
26
|
+
context: .
|
27
|
+
command: yard server --bind 0.0.0.0 --reload
|
28
|
+
environment:
|
29
|
+
BUNDLE_TO: /usr/local/bundle
|
30
|
+
volumes:
|
31
|
+
- .:/usr/src/app
|
32
|
+
- bundle_config:/home/src/app/.bundle
|
33
|
+
- bundle:/usr/local/bundle
|
34
|
+
ports:
|
35
|
+
- 8808:8808
|
36
|
+
redis:
|
37
|
+
image: redis:5.0
|
38
|
+
volumes:
|
39
|
+
- redis_data:/data
|
40
|
+
postgres:
|
41
|
+
image: postgres:11.0
|
42
|
+
volumes:
|
43
|
+
- postgres_data:/var/lib/postgresql/data
|
44
|
+
environment:
|
45
|
+
- POSTGRES_PASSWORD=1234
|
46
|
+
- POSTGRES_DB=message_bus_test
|
data/examples/bench/config.ru
CHANGED
@@ -4,17 +4,16 @@ require 'message_bus'
|
|
4
4
|
require 'stackprof'
|
5
5
|
|
6
6
|
if defined?(PhusionPassenger)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
7
|
+
PhusionPassenger.on_event(:starting_worker_process) do |forked|
|
8
|
+
if forked
|
9
|
+
# We're in smart spawning mode.
|
10
|
+
MessageBus.after_fork
|
11
|
+
else
|
12
|
+
# We're in conservative spawning mode. We don't need to do anything.
|
14
13
|
end
|
14
|
+
end
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
17
|
# require 'rack-mini-profiler'
|
19
18
|
|
20
19
|
# Rack::MiniProfiler.config.storage = Rack::MiniProfiler::MemoryStore
|
@@ -30,4 +29,4 @@ end
|
|
30
29
|
MessageBus.long_polling_interval = 1000 * 2
|
31
30
|
MessageBus.max_active_clients = 10000
|
32
31
|
use MessageBus::Rack::Middleware
|
33
|
-
run lambda { |
|
32
|
+
run lambda { |_env| [200, { "Content-Type" => "text/html" }, ["Howdy"]] }
|
data/examples/chat/chat.rb
CHANGED
@@ -7,13 +7,12 @@ require 'json'
|
|
7
7
|
|
8
8
|
$online = Hash.new
|
9
9
|
|
10
|
-
|
11
10
|
MessageBus.subscribe "/presence" do |msg|
|
12
11
|
if user = msg.data["enter"]
|
13
|
-
|
12
|
+
$online[user] = Time.now
|
14
13
|
end
|
15
14
|
if user = msg.data["leave"]
|
16
|
-
|
15
|
+
$online.delete user
|
17
16
|
end
|
18
17
|
end
|
19
18
|
|
@@ -22,7 +21,7 @@ MessageBus.user_id_lookup do |env|
|
|
22
21
|
name = env["HTTP_X_NAME"]
|
23
22
|
if name
|
24
23
|
unless $online[name]
|
25
|
-
MessageBus.publish "/presence",
|
24
|
+
MessageBus.publish "/presence", enter: name
|
26
25
|
end
|
27
26
|
$online[name] = Time.now
|
28
27
|
end
|
@@ -30,10 +29,10 @@ MessageBus.user_id_lookup do |env|
|
|
30
29
|
end
|
31
30
|
|
32
31
|
def expire_old_sessions
|
33
|
-
$online.each do |name,time|
|
34
|
-
if (Time.now - (5*60)) > time
|
35
|
-
|
36
|
-
|
32
|
+
$online.each do |name, time|
|
33
|
+
if (Time.now - (5 * 60)) > time
|
34
|
+
puts "forcing leave for #{name} session timed out"
|
35
|
+
MessageBus.publish "/presence", leave: name
|
37
36
|
end
|
38
37
|
end
|
39
38
|
rescue => e
|
@@ -48,8 +47,7 @@ Thread.new do
|
|
48
47
|
end
|
49
48
|
|
50
49
|
class Chat < Sinatra::Base
|
51
|
-
|
52
|
-
set :public_folder, File.expand_path('../../../assets',__FILE__)
|
50
|
+
set :public_folder, File.expand_path('../../../assets', __FILE__)
|
53
51
|
|
54
52
|
use MessageBus::Rack::Middleware
|
55
53
|
|
@@ -60,166 +58,165 @@ class Chat < Sinatra::Base
|
|
60
58
|
name = "#{params["name"]}#{i}"
|
61
59
|
i += 1
|
62
60
|
end
|
63
|
-
MessageBus.publish '/presence',
|
64
|
-
{users: $online.keys, name: name}.to_json
|
61
|
+
MessageBus.publish '/presence', enter: name
|
62
|
+
{ users: $online.keys, name: name }.to_json
|
65
63
|
end
|
66
64
|
|
67
65
|
post '/leave' do
|
68
|
-
#puts "Got leave for #{params["name"]}"
|
69
|
-
MessageBus.publish '/presence',
|
66
|
+
# puts "Got leave for #{params["name"]}"
|
67
|
+
MessageBus.publish '/presence', leave: params["name"]
|
70
68
|
end
|
71
69
|
|
72
70
|
post '/message' do
|
73
|
-
msg = {data: params["data"][0..500], name: params["name"][0..100]}
|
71
|
+
msg = { data: params["data"][0..500], name: params["name"][0..100] }
|
74
72
|
MessageBus.publish '/message', msg
|
75
73
|
|
76
74
|
"OK"
|
77
75
|
end
|
78
76
|
|
79
77
|
get '/' do
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
<
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
78
|
+
<<~HTML
|
79
|
+
|
80
|
+
<html>
|
81
|
+
<head>
|
82
|
+
<script src="/jquery-1.8.2.js"></script>
|
83
|
+
<script src="/message-bus.js"></script>
|
84
|
+
<style>
|
85
|
+
#panel { position: fixed; bottom: 0; background-color: #FFFFFF; }
|
86
|
+
#panel form { margin-bottom: 4px; }
|
87
|
+
#panel button, #panel textarea { height: 40px }
|
88
|
+
#panel button { width: 100px; vertical-align: top; }
|
89
|
+
#messages { padding-bottom: 40px; }
|
90
|
+
.hidden { display: none; }
|
91
|
+
#users {
|
92
|
+
position: fixed; top: 0; right: 0; width: 160px; background-color: #fafafa; height: 100%;
|
93
|
+
border-left: 1px solid #dfdfdf;
|
94
|
+
padding: 5px;
|
95
|
+
}
|
96
|
+
#users ul li { margin: 0; padding: 0; }
|
97
|
+
#users ul li.me { font-weight: bold; }
|
98
|
+
#users ul { list-style: none; margin 0; padding: 0; }
|
99
|
+
body { padding-right: 160px; }
|
100
|
+
</style>
|
101
|
+
</head>
|
102
|
+
<body>
|
103
|
+
<p>This is a trivial chat demo... It is implemented as a <a href="https://github.com/SamSaffron/message_bus/blob/master/examples/chat/chat.rb">Sinatra app</a>. The <a href="https://github.com/SamSaffron/message_bus">message_bus</a> can easily be added to any Rails/Rack app. <small>This app can be deployed with <a href="https://github.com/discourse/discourse_docker">Discourse Docker</a> using <a href="https://github.com/SamSaffron/message_bus/blob/master/examples/chat/docker_container/chat.yml">this template</a>.</small></p>
|
104
|
+
<div id='messages' class="hidden"></div>
|
105
|
+
<div id='users' class="hidden">
|
106
|
+
<h3>Online</h3>
|
107
|
+
<ul></ul>
|
108
|
+
</div>
|
109
|
+
<div id='panel' class="hidden">
|
110
|
+
<form>
|
111
|
+
<textarea cols=80 rows=2></textarea>
|
112
|
+
<button id="send">send</button>
|
113
|
+
</form>
|
114
|
+
</div>
|
115
|
+
<div id='your-name'>Enter your name: <input type='text'/>
|
116
|
+
|
117
|
+
<script>
|
118
|
+
$(function() {
|
119
|
+
var name;
|
120
|
+
|
121
|
+
MessageBus.headers["X-NAME"] = name;
|
122
|
+
|
123
|
+
var enter = function(name, opts) {
|
124
|
+
if (opts && opts.check) {
|
125
|
+
var found = false;
|
126
|
+
$('#users ul li').each(function(){
|
127
|
+
found = found || ($(this).text().trim() === name.trim());
|
128
|
+
if (found && opts.me) {
|
129
|
+
$(this).remove();
|
130
|
+
found = false;
|
131
|
+
}
|
132
|
+
return !found;
|
133
|
+
});
|
134
|
+
if (found) { return; }
|
135
|
+
}
|
136
|
+
|
137
|
+
var li = $('<li></li>');
|
138
|
+
li.text(name);
|
139
|
+
if (opts && opts.me) {
|
140
|
+
li.addClass("me");
|
141
|
+
$('#users ul').prepend(li);
|
142
|
+
} else {
|
143
|
+
$('#users ul').append(li);
|
144
|
+
}
|
145
|
+
};
|
146
|
+
|
147
|
+
$('#messages, #panel').addClass('hidden');
|
148
|
+
|
149
|
+
$('#your-name input').keyup(function(e){
|
150
|
+
if(e.keyCode == 13) {
|
151
|
+
name = $(this).val();
|
152
|
+
$.post("/enter", { name: name}, null, "json" ).success(function(data){
|
153
|
+
$.each(data.users,function(idx, name){
|
154
|
+
enter(name);
|
155
|
+
});
|
156
|
+
name = data.name;
|
157
|
+
enter(name, {check: true, me: true});
|
158
|
+
});
|
159
|
+
$('#your-name').addClass('hidden');
|
160
|
+
$('#messages, #panel, #users').removeClass('hidden');
|
161
|
+
$(document.body).scrollTop(document.body.scrollHeight);
|
162
|
+
|
163
|
+
window.onbeforeunload = function(){
|
164
|
+
$.post("/leave", { name: name });
|
165
|
+
};
|
166
|
+
|
167
|
+
MessageBus.subscribe("/presence", function(msg){
|
168
|
+
if (msg.enter) {
|
169
|
+
enter(msg.enter, {check: true});
|
170
|
+
}
|
171
|
+
if (msg.leave) {
|
172
|
+
$('#users ul li').each(function(){
|
173
|
+
if ($(this).text() === msg.leave) {
|
174
|
+
$(this).remove()
|
175
|
+
}
|
176
|
+
});
|
177
|
+
}
|
178
|
+
});
|
134
179
|
}
|
135
|
-
return !found;
|
136
|
-
});
|
137
|
-
if (found) { return; }
|
138
|
-
}
|
139
|
-
|
140
|
-
var li = $('<li></li>');
|
141
|
-
li.text(name);
|
142
|
-
if (opts && opts.me) {
|
143
|
-
li.addClass("me");
|
144
|
-
$('#users ul').prepend(li);
|
145
|
-
} else {
|
146
|
-
$('#users ul').append(li);
|
147
|
-
}
|
148
|
-
};
|
149
|
-
|
150
|
-
$('#messages, #panel').addClass('hidden');
|
151
|
-
|
152
|
-
$('#your-name input').keyup(function(e){
|
153
|
-
if(e.keyCode == 13) {
|
154
|
-
name = $(this).val();
|
155
|
-
$.post("/enter", { name: name}, null, "json" ).success(function(data){
|
156
|
-
$.each(data.users,function(idx, name){
|
157
|
-
enter(name);
|
158
180
|
});
|
159
|
-
name = data.name;
|
160
|
-
enter(name, {check: true, me: true});
|
161
|
-
});
|
162
|
-
$('#your-name').addClass('hidden');
|
163
|
-
$('#messages, #panel, #users').removeClass('hidden');
|
164
|
-
$(document.body).scrollTop(document.body.scrollHeight);
|
165
181
|
|
166
|
-
|
167
|
-
|
168
|
-
|
182
|
+
var safe = function(html){
|
183
|
+
return String(html).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
184
|
+
};
|
185
|
+
|
186
|
+
MessageBus.subscribe("/message", function(msg){
|
187
|
+
$('#messages').append("<p>"+ safe(msg.name) + " said: " + safe(msg.data) + "</p>");
|
188
|
+
$(document.body).scrollTop(document.body.scrollHeight);
|
189
|
+
}, 0); // last id is zero, so getting backlog
|
190
|
+
|
191
|
+
var submit = function(){
|
192
|
+
var val = $('form textarea').val().trim();
|
193
|
+
if (val.length === 0) {
|
194
|
+
return;
|
195
|
+
}
|
196
|
+
|
197
|
+
if (val.length > 500) {
|
198
|
+
alert("message too long");
|
199
|
+
return false;
|
200
|
+
} else {
|
201
|
+
$.post("/message", { data: val, name: name} );
|
202
|
+
}
|
203
|
+
$('textarea').val("");
|
204
|
+
};
|
205
|
+
|
206
|
+
$('#send').click(function(){submit(); return false;});
|
207
|
+
|
208
|
+
$('textarea').keyup(function(e){
|
209
|
+
if(e.keyCode == 13) {
|
210
|
+
submit();
|
211
|
+
}
|
212
|
+
});
|
169
213
|
|
170
|
-
MessageBus.subscribe("/presence", function(msg){
|
171
|
-
if (msg.enter) {
|
172
|
-
enter(msg.enter, {check: true});
|
173
|
-
}
|
174
|
-
if (msg.leave) {
|
175
|
-
$('#users ul li').each(function(){
|
176
|
-
if ($(this).text() === msg.leave) {
|
177
|
-
$(this).remove()
|
178
|
-
}
|
179
|
-
});
|
180
|
-
}
|
181
214
|
});
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
};
|
188
|
-
|
189
|
-
MessageBus.subscribe("/message", function(msg){
|
190
|
-
$('#messages').append("<p>"+ safe(msg.name) + " said: " + safe(msg.data) + "</p>");
|
191
|
-
$(document.body).scrollTop(document.body.scrollHeight);
|
192
|
-
}, 0); // last id is zero, so getting backlog
|
193
|
-
|
194
|
-
var submit = function(){
|
195
|
-
var val = $('form textarea').val().trim();
|
196
|
-
if (val.length === 0) {
|
197
|
-
return;
|
198
|
-
}
|
199
|
-
|
200
|
-
if (val.length > 500) {
|
201
|
-
alert("message too long");
|
202
|
-
return false;
|
203
|
-
} else {
|
204
|
-
$.post("/message", { data: val, name: name} );
|
205
|
-
}
|
206
|
-
$('textarea').val("");
|
207
|
-
};
|
208
|
-
|
209
|
-
$('#send').click(function(){submit(); return false;});
|
210
|
-
|
211
|
-
$('textarea').keyup(function(e){
|
212
|
-
if(e.keyCode == 13) {
|
213
|
-
submit();
|
214
|
-
}
|
215
|
-
});
|
216
|
-
|
217
|
-
});
|
218
|
-
</script>
|
219
|
-
</body>
|
220
|
-
</html>
|
221
|
-
|
222
|
-
HTML
|
215
|
+
</script>
|
216
|
+
</body>
|
217
|
+
</html>
|
218
|
+
|
219
|
+
HTML
|
223
220
|
end
|
224
221
|
|
225
222
|
run! if app_file == $0
|