actioncable 5.2.7.1 → 6.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +53 -47
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +3 -546
  5. data/app/assets/javascripts/action_cable.js +574 -0
  6. data/lib/action_cable/channel/base.rb +10 -4
  7. data/lib/action_cable/channel/broadcasting.rb +18 -8
  8. data/lib/action_cable/channel/naming.rb +1 -1
  9. data/lib/action_cable/channel/streams.rb +30 -4
  10. data/lib/action_cable/channel/test_case.rb +310 -0
  11. data/lib/action_cable/channel.rb +1 -0
  12. data/lib/action_cable/connection/authorization.rb +1 -1
  13. data/lib/action_cable/connection/base.rb +13 -7
  14. data/lib/action_cable/connection/message_buffer.rb +1 -4
  15. data/lib/action_cable/connection/stream.rb +4 -2
  16. data/lib/action_cable/connection/subscriptions.rb +2 -5
  17. data/lib/action_cable/connection/test_case.rb +234 -0
  18. data/lib/action_cable/connection/web_socket.rb +1 -3
  19. data/lib/action_cable/connection.rb +1 -0
  20. data/lib/action_cable/engine.rb +1 -1
  21. data/lib/action_cable/gem_version.rb +4 -4
  22. data/lib/action_cable/helpers/action_cable_helper.rb +3 -3
  23. data/lib/action_cable/remote_connections.rb +1 -1
  24. data/lib/action_cable/server/base.rb +9 -4
  25. data/lib/action_cable/server/broadcasting.rb +1 -1
  26. data/lib/action_cable/server/worker.rb +6 -8
  27. data/lib/action_cable/server.rb +0 -1
  28. data/lib/action_cable/subscription_adapter/base.rb +4 -0
  29. data/lib/action_cable/subscription_adapter/postgresql.rb +28 -9
  30. data/lib/action_cable/subscription_adapter/redis.rb +4 -2
  31. data/lib/action_cable/subscription_adapter/test.rb +40 -0
  32. data/lib/action_cable/subscription_adapter.rb +1 -0
  33. data/lib/action_cable/test_case.rb +11 -0
  34. data/lib/action_cable/test_helper.rb +133 -0
  35. data/lib/action_cable.rb +15 -7
  36. data/lib/rails/generators/channel/USAGE +5 -6
  37. data/lib/rails/generators/channel/channel_generator.rb +6 -3
  38. data/lib/rails/generators/channel/templates/{assets → javascript}/channel.js.tt +6 -4
  39. data/lib/rails/generators/channel/templates/javascript/consumer.js.tt +6 -0
  40. data/lib/rails/generators/channel/templates/javascript/index.js.tt +5 -0
  41. data/lib/rails/generators/test_unit/channel_generator.rb +20 -0
  42. data/lib/rails/generators/test_unit/templates/channel_test.rb.tt +8 -0
  43. metadata +41 -16
  44. data/lib/assets/compiled/action_cable.js +0 -601
  45. data/lib/rails/generators/channel/templates/assets/cable.js.tt +0 -13
  46. data/lib/rails/generators/channel/templates/assets/channel.coffee.tt +0 -14
@@ -1,601 +0,0 @@
1
- (function() {
2
- var context = this;
3
-
4
- (function() {
5
- (function() {
6
- var slice = [].slice;
7
-
8
- this.ActionCable = {
9
- INTERNAL: {
10
- "message_types": {
11
- "welcome": "welcome",
12
- "ping": "ping",
13
- "confirmation": "confirm_subscription",
14
- "rejection": "reject_subscription"
15
- },
16
- "default_mount_path": "/cable",
17
- "protocols": ["actioncable-v1-json", "actioncable-unsupported"]
18
- },
19
- WebSocket: window.WebSocket,
20
- logger: window.console,
21
- createConsumer: function(url) {
22
- var ref;
23
- if (url == null) {
24
- url = (ref = this.getConfig("url")) != null ? ref : this.INTERNAL.default_mount_path;
25
- }
26
- return new ActionCable.Consumer(this.createWebSocketURL(url));
27
- },
28
- getConfig: function(name) {
29
- var element;
30
- element = document.head.querySelector("meta[name='action-cable-" + name + "']");
31
- return element != null ? element.getAttribute("content") : void 0;
32
- },
33
- createWebSocketURL: function(url) {
34
- var a;
35
- if (url && !/^wss?:/i.test(url)) {
36
- a = document.createElement("a");
37
- a.href = url;
38
- a.href = a.href;
39
- a.protocol = a.protocol.replace("http", "ws");
40
- return a.href;
41
- } else {
42
- return url;
43
- }
44
- },
45
- startDebugging: function() {
46
- return this.debugging = true;
47
- },
48
- stopDebugging: function() {
49
- return this.debugging = null;
50
- },
51
- log: function() {
52
- var messages, ref;
53
- messages = 1 <= arguments.length ? slice.call(arguments, 0) : [];
54
- if (this.debugging) {
55
- messages.push(Date.now());
56
- return (ref = this.logger).log.apply(ref, ["[ActionCable]"].concat(slice.call(messages)));
57
- }
58
- }
59
- };
60
-
61
- }).call(this);
62
- }).call(context);
63
-
64
- var ActionCable = context.ActionCable;
65
-
66
- (function() {
67
- (function() {
68
- var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
69
-
70
- ActionCable.ConnectionMonitor = (function() {
71
- var clamp, now, secondsSince;
72
-
73
- ConnectionMonitor.pollInterval = {
74
- min: 3,
75
- max: 30
76
- };
77
-
78
- ConnectionMonitor.staleThreshold = 6;
79
-
80
- function ConnectionMonitor(connection) {
81
- this.connection = connection;
82
- this.visibilityDidChange = bind(this.visibilityDidChange, this);
83
- this.reconnectAttempts = 0;
84
- }
85
-
86
- ConnectionMonitor.prototype.start = function() {
87
- if (!this.isRunning()) {
88
- this.startedAt = now();
89
- delete this.stoppedAt;
90
- this.startPolling();
91
- document.addEventListener("visibilitychange", this.visibilityDidChange);
92
- return ActionCable.log("ConnectionMonitor started. pollInterval = " + (this.getPollInterval()) + " ms");
93
- }
94
- };
95
-
96
- ConnectionMonitor.prototype.stop = function() {
97
- if (this.isRunning()) {
98
- this.stoppedAt = now();
99
- this.stopPolling();
100
- document.removeEventListener("visibilitychange", this.visibilityDidChange);
101
- return ActionCable.log("ConnectionMonitor stopped");
102
- }
103
- };
104
-
105
- ConnectionMonitor.prototype.isRunning = function() {
106
- return (this.startedAt != null) && (this.stoppedAt == null);
107
- };
108
-
109
- ConnectionMonitor.prototype.recordPing = function() {
110
- return this.pingedAt = now();
111
- };
112
-
113
- ConnectionMonitor.prototype.recordConnect = function() {
114
- this.reconnectAttempts = 0;
115
- this.recordPing();
116
- delete this.disconnectedAt;
117
- return ActionCable.log("ConnectionMonitor recorded connect");
118
- };
119
-
120
- ConnectionMonitor.prototype.recordDisconnect = function() {
121
- this.disconnectedAt = now();
122
- return ActionCable.log("ConnectionMonitor recorded disconnect");
123
- };
124
-
125
- ConnectionMonitor.prototype.startPolling = function() {
126
- this.stopPolling();
127
- return this.poll();
128
- };
129
-
130
- ConnectionMonitor.prototype.stopPolling = function() {
131
- return clearTimeout(this.pollTimeout);
132
- };
133
-
134
- ConnectionMonitor.prototype.poll = function() {
135
- return this.pollTimeout = setTimeout((function(_this) {
136
- return function() {
137
- _this.reconnectIfStale();
138
- return _this.poll();
139
- };
140
- })(this), this.getPollInterval());
141
- };
142
-
143
- ConnectionMonitor.prototype.getPollInterval = function() {
144
- var interval, max, min, ref;
145
- ref = this.constructor.pollInterval, min = ref.min, max = ref.max;
146
- interval = 5 * Math.log(this.reconnectAttempts + 1);
147
- return Math.round(clamp(interval, min, max) * 1000);
148
- };
149
-
150
- ConnectionMonitor.prototype.reconnectIfStale = function() {
151
- if (this.connectionIsStale()) {
152
- ActionCable.log("ConnectionMonitor detected stale connection. reconnectAttempts = " + this.reconnectAttempts + ", pollInterval = " + (this.getPollInterval()) + " ms, time disconnected = " + (secondsSince(this.disconnectedAt)) + " s, stale threshold = " + this.constructor.staleThreshold + " s");
153
- this.reconnectAttempts++;
154
- if (this.disconnectedRecently()) {
155
- return ActionCable.log("ConnectionMonitor skipping reopening recent disconnect");
156
- } else {
157
- ActionCable.log("ConnectionMonitor reopening");
158
- return this.connection.reopen();
159
- }
160
- }
161
- };
162
-
163
- ConnectionMonitor.prototype.connectionIsStale = function() {
164
- var ref;
165
- return secondsSince((ref = this.pingedAt) != null ? ref : this.startedAt) > this.constructor.staleThreshold;
166
- };
167
-
168
- ConnectionMonitor.prototype.disconnectedRecently = function() {
169
- return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold;
170
- };
171
-
172
- ConnectionMonitor.prototype.visibilityDidChange = function() {
173
- if (document.visibilityState === "visible") {
174
- return setTimeout((function(_this) {
175
- return function() {
176
- if (_this.connectionIsStale() || !_this.connection.isOpen()) {
177
- ActionCable.log("ConnectionMonitor reopening stale connection on visibilitychange. visbilityState = " + document.visibilityState);
178
- return _this.connection.reopen();
179
- }
180
- };
181
- })(this), 200);
182
- }
183
- };
184
-
185
- now = function() {
186
- return new Date().getTime();
187
- };
188
-
189
- secondsSince = function(time) {
190
- return (now() - time) / 1000;
191
- };
192
-
193
- clamp = function(number, min, max) {
194
- return Math.max(min, Math.min(max, number));
195
- };
196
-
197
- return ConnectionMonitor;
198
-
199
- })();
200
-
201
- }).call(this);
202
- (function() {
203
- var i, message_types, protocols, ref, supportedProtocols, unsupportedProtocol,
204
- slice = [].slice,
205
- bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
206
- indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
207
-
208
- ref = ActionCable.INTERNAL, message_types = ref.message_types, protocols = ref.protocols;
209
-
210
- supportedProtocols = 2 <= protocols.length ? slice.call(protocols, 0, i = protocols.length - 1) : (i = 0, []), unsupportedProtocol = protocols[i++];
211
-
212
- ActionCable.Connection = (function() {
213
- Connection.reopenDelay = 500;
214
-
215
- function Connection(consumer) {
216
- this.consumer = consumer;
217
- this.open = bind(this.open, this);
218
- this.subscriptions = this.consumer.subscriptions;
219
- this.monitor = new ActionCable.ConnectionMonitor(this);
220
- this.disconnected = true;
221
- }
222
-
223
- Connection.prototype.send = function(data) {
224
- if (this.isOpen()) {
225
- this.webSocket.send(JSON.stringify(data));
226
- return true;
227
- } else {
228
- return false;
229
- }
230
- };
231
-
232
- Connection.prototype.open = function() {
233
- if (this.isActive()) {
234
- ActionCable.log("Attempted to open WebSocket, but existing socket is " + (this.getState()));
235
- return false;
236
- } else {
237
- ActionCable.log("Opening WebSocket, current state is " + (this.getState()) + ", subprotocols: " + protocols);
238
- if (this.webSocket != null) {
239
- this.uninstallEventHandlers();
240
- }
241
- this.webSocket = new ActionCable.WebSocket(this.consumer.url, protocols);
242
- this.installEventHandlers();
243
- this.monitor.start();
244
- return true;
245
- }
246
- };
247
-
248
- Connection.prototype.close = function(arg) {
249
- var allowReconnect, ref1;
250
- allowReconnect = (arg != null ? arg : {
251
- allowReconnect: true
252
- }).allowReconnect;
253
- if (!allowReconnect) {
254
- this.monitor.stop();
255
- }
256
- if (this.isActive()) {
257
- return (ref1 = this.webSocket) != null ? ref1.close() : void 0;
258
- }
259
- };
260
-
261
- Connection.prototype.reopen = function() {
262
- var error;
263
- ActionCable.log("Reopening WebSocket, current state is " + (this.getState()));
264
- if (this.isActive()) {
265
- try {
266
- return this.close();
267
- } catch (error1) {
268
- error = error1;
269
- return ActionCable.log("Failed to reopen WebSocket", error);
270
- } finally {
271
- ActionCable.log("Reopening WebSocket in " + this.constructor.reopenDelay + "ms");
272
- setTimeout(this.open, this.constructor.reopenDelay);
273
- }
274
- } else {
275
- return this.open();
276
- }
277
- };
278
-
279
- Connection.prototype.getProtocol = function() {
280
- var ref1;
281
- return (ref1 = this.webSocket) != null ? ref1.protocol : void 0;
282
- };
283
-
284
- Connection.prototype.isOpen = function() {
285
- return this.isState("open");
286
- };
287
-
288
- Connection.prototype.isActive = function() {
289
- return this.isState("open", "connecting");
290
- };
291
-
292
- Connection.prototype.isProtocolSupported = function() {
293
- var ref1;
294
- return ref1 = this.getProtocol(), indexOf.call(supportedProtocols, ref1) >= 0;
295
- };
296
-
297
- Connection.prototype.isState = function() {
298
- var ref1, states;
299
- states = 1 <= arguments.length ? slice.call(arguments, 0) : [];
300
- return ref1 = this.getState(), indexOf.call(states, ref1) >= 0;
301
- };
302
-
303
- Connection.prototype.getState = function() {
304
- var ref1, state, value;
305
- for (state in WebSocket) {
306
- value = WebSocket[state];
307
- if (value === ((ref1 = this.webSocket) != null ? ref1.readyState : void 0)) {
308
- return state.toLowerCase();
309
- }
310
- }
311
- return null;
312
- };
313
-
314
- Connection.prototype.installEventHandlers = function() {
315
- var eventName, handler;
316
- for (eventName in this.events) {
317
- handler = this.events[eventName].bind(this);
318
- this.webSocket["on" + eventName] = handler;
319
- }
320
- };
321
-
322
- Connection.prototype.uninstallEventHandlers = function() {
323
- var eventName;
324
- for (eventName in this.events) {
325
- this.webSocket["on" + eventName] = function() {};
326
- }
327
- };
328
-
329
- Connection.prototype.events = {
330
- message: function(event) {
331
- var identifier, message, ref1, type;
332
- if (!this.isProtocolSupported()) {
333
- return;
334
- }
335
- ref1 = JSON.parse(event.data), identifier = ref1.identifier, message = ref1.message, type = ref1.type;
336
- switch (type) {
337
- case message_types.welcome:
338
- this.monitor.recordConnect();
339
- return this.subscriptions.reload();
340
- case message_types.ping:
341
- return this.monitor.recordPing();
342
- case message_types.confirmation:
343
- return this.subscriptions.notify(identifier, "connected");
344
- case message_types.rejection:
345
- return this.subscriptions.reject(identifier);
346
- default:
347
- return this.subscriptions.notify(identifier, "received", message);
348
- }
349
- },
350
- open: function() {
351
- ActionCable.log("WebSocket onopen event, using '" + (this.getProtocol()) + "' subprotocol");
352
- this.disconnected = false;
353
- if (!this.isProtocolSupported()) {
354
- ActionCable.log("Protocol is unsupported. Stopping monitor and disconnecting.");
355
- return this.close({
356
- allowReconnect: false
357
- });
358
- }
359
- },
360
- close: function(event) {
361
- ActionCable.log("WebSocket onclose event");
362
- if (this.disconnected) {
363
- return;
364
- }
365
- this.disconnected = true;
366
- this.monitor.recordDisconnect();
367
- return this.subscriptions.notifyAll("disconnected", {
368
- willAttemptReconnect: this.monitor.isRunning()
369
- });
370
- },
371
- error: function() {
372
- return ActionCable.log("WebSocket onerror event");
373
- }
374
- };
375
-
376
- return Connection;
377
-
378
- })();
379
-
380
- }).call(this);
381
- (function() {
382
- var slice = [].slice;
383
-
384
- ActionCable.Subscriptions = (function() {
385
- function Subscriptions(consumer) {
386
- this.consumer = consumer;
387
- this.subscriptions = [];
388
- }
389
-
390
- Subscriptions.prototype.create = function(channelName, mixin) {
391
- var channel, params, subscription;
392
- channel = channelName;
393
- params = typeof channel === "object" ? channel : {
394
- channel: channel
395
- };
396
- subscription = new ActionCable.Subscription(this.consumer, params, mixin);
397
- return this.add(subscription);
398
- };
399
-
400
- Subscriptions.prototype.add = function(subscription) {
401
- this.subscriptions.push(subscription);
402
- this.consumer.ensureActiveConnection();
403
- this.notify(subscription, "initialized");
404
- this.sendCommand(subscription, "subscribe");
405
- return subscription;
406
- };
407
-
408
- Subscriptions.prototype.remove = function(subscription) {
409
- this.forget(subscription);
410
- if (!this.findAll(subscription.identifier).length) {
411
- this.sendCommand(subscription, "unsubscribe");
412
- }
413
- return subscription;
414
- };
415
-
416
- Subscriptions.prototype.reject = function(identifier) {
417
- var i, len, ref, results, subscription;
418
- ref = this.findAll(identifier);
419
- results = [];
420
- for (i = 0, len = ref.length; i < len; i++) {
421
- subscription = ref[i];
422
- this.forget(subscription);
423
- this.notify(subscription, "rejected");
424
- results.push(subscription);
425
- }
426
- return results;
427
- };
428
-
429
- Subscriptions.prototype.forget = function(subscription) {
430
- var s;
431
- this.subscriptions = (function() {
432
- var i, len, ref, results;
433
- ref = this.subscriptions;
434
- results = [];
435
- for (i = 0, len = ref.length; i < len; i++) {
436
- s = ref[i];
437
- if (s !== subscription) {
438
- results.push(s);
439
- }
440
- }
441
- return results;
442
- }).call(this);
443
- return subscription;
444
- };
445
-
446
- Subscriptions.prototype.findAll = function(identifier) {
447
- var i, len, ref, results, s;
448
- ref = this.subscriptions;
449
- results = [];
450
- for (i = 0, len = ref.length; i < len; i++) {
451
- s = ref[i];
452
- if (s.identifier === identifier) {
453
- results.push(s);
454
- }
455
- }
456
- return results;
457
- };
458
-
459
- Subscriptions.prototype.reload = function() {
460
- var i, len, ref, results, subscription;
461
- ref = this.subscriptions;
462
- results = [];
463
- for (i = 0, len = ref.length; i < len; i++) {
464
- subscription = ref[i];
465
- results.push(this.sendCommand(subscription, "subscribe"));
466
- }
467
- return results;
468
- };
469
-
470
- Subscriptions.prototype.notifyAll = function() {
471
- var args, callbackName, i, len, ref, results, subscription;
472
- callbackName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
473
- ref = this.subscriptions;
474
- results = [];
475
- for (i = 0, len = ref.length; i < len; i++) {
476
- subscription = ref[i];
477
- results.push(this.notify.apply(this, [subscription, callbackName].concat(slice.call(args))));
478
- }
479
- return results;
480
- };
481
-
482
- Subscriptions.prototype.notify = function() {
483
- var args, callbackName, i, len, results, subscription, subscriptions;
484
- subscription = arguments[0], callbackName = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
485
- if (typeof subscription === "string") {
486
- subscriptions = this.findAll(subscription);
487
- } else {
488
- subscriptions = [subscription];
489
- }
490
- results = [];
491
- for (i = 0, len = subscriptions.length; i < len; i++) {
492
- subscription = subscriptions[i];
493
- results.push(typeof subscription[callbackName] === "function" ? subscription[callbackName].apply(subscription, args) : void 0);
494
- }
495
- return results;
496
- };
497
-
498
- Subscriptions.prototype.sendCommand = function(subscription, command) {
499
- var identifier;
500
- identifier = subscription.identifier;
501
- return this.consumer.send({
502
- command: command,
503
- identifier: identifier
504
- });
505
- };
506
-
507
- return Subscriptions;
508
-
509
- })();
510
-
511
- }).call(this);
512
- (function() {
513
- ActionCable.Subscription = (function() {
514
- var extend;
515
-
516
- function Subscription(consumer, params, mixin) {
517
- this.consumer = consumer;
518
- if (params == null) {
519
- params = {};
520
- }
521
- this.identifier = JSON.stringify(params);
522
- extend(this, mixin);
523
- }
524
-
525
- Subscription.prototype.perform = function(action, data) {
526
- if (data == null) {
527
- data = {};
528
- }
529
- data.action = action;
530
- return this.send(data);
531
- };
532
-
533
- Subscription.prototype.send = function(data) {
534
- return this.consumer.send({
535
- command: "message",
536
- identifier: this.identifier,
537
- data: JSON.stringify(data)
538
- });
539
- };
540
-
541
- Subscription.prototype.unsubscribe = function() {
542
- return this.consumer.subscriptions.remove(this);
543
- };
544
-
545
- extend = function(object, properties) {
546
- var key, value;
547
- if (properties != null) {
548
- for (key in properties) {
549
- value = properties[key];
550
- object[key] = value;
551
- }
552
- }
553
- return object;
554
- };
555
-
556
- return Subscription;
557
-
558
- })();
559
-
560
- }).call(this);
561
- (function() {
562
- ActionCable.Consumer = (function() {
563
- function Consumer(url) {
564
- this.url = url;
565
- this.subscriptions = new ActionCable.Subscriptions(this);
566
- this.connection = new ActionCable.Connection(this);
567
- }
568
-
569
- Consumer.prototype.send = function(data) {
570
- return this.connection.send(data);
571
- };
572
-
573
- Consumer.prototype.connect = function() {
574
- return this.connection.open();
575
- };
576
-
577
- Consumer.prototype.disconnect = function() {
578
- return this.connection.close({
579
- allowReconnect: false
580
- });
581
- };
582
-
583
- Consumer.prototype.ensureActiveConnection = function() {
584
- if (!this.connection.isActive()) {
585
- return this.connection.open();
586
- }
587
- };
588
-
589
- return Consumer;
590
-
591
- })();
592
-
593
- }).call(this);
594
- }).call(this);
595
-
596
- if (typeof module === "object" && module.exports) {
597
- module.exports = ActionCable;
598
- } else if (typeof define === "function" && define.amd) {
599
- define(ActionCable);
600
- }
601
- }).call(this);
@@ -1,13 +0,0 @@
1
- // Action Cable provides the framework to deal with WebSockets in Rails.
2
- // You can generate new channels where WebSocket features live using the `rails generate channel` command.
3
- //
4
- //= require action_cable
5
- //= require_self
6
- //= require_tree ./channels
7
-
8
- (function() {
9
- this.App || (this.App = {});
10
-
11
- App.cable = ActionCable.createConsumer();
12
-
13
- }).call(this);
@@ -1,14 +0,0 @@
1
- App.<%= class_name.underscore %> = App.cable.subscriptions.create "<%= class_name %>Channel",
2
- connected: ->
3
- # Called when the subscription is ready for use on the server
4
-
5
- disconnected: ->
6
- # Called when the subscription has been terminated by the server
7
-
8
- received: (data) ->
9
- # Called when there's incoming data on the websocket for this channel
10
- <% actions.each do |action| -%>
11
-
12
- <%= action %>: ->
13
- @perform '<%= action %>'
14
- <% end -%>