actioncable 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +169 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +24 -0
  5. data/app/assets/javascripts/action_cable.js +517 -0
  6. data/lib/action_cable.rb +62 -0
  7. data/lib/action_cable/channel.rb +17 -0
  8. data/lib/action_cable/channel/base.rb +311 -0
  9. data/lib/action_cable/channel/broadcasting.rb +41 -0
  10. data/lib/action_cable/channel/callbacks.rb +37 -0
  11. data/lib/action_cable/channel/naming.rb +25 -0
  12. data/lib/action_cable/channel/periodic_timers.rb +78 -0
  13. data/lib/action_cable/channel/streams.rb +176 -0
  14. data/lib/action_cable/channel/test_case.rb +310 -0
  15. data/lib/action_cable/connection.rb +22 -0
  16. data/lib/action_cable/connection/authorization.rb +15 -0
  17. data/lib/action_cable/connection/base.rb +264 -0
  18. data/lib/action_cable/connection/client_socket.rb +157 -0
  19. data/lib/action_cable/connection/identification.rb +47 -0
  20. data/lib/action_cable/connection/internal_channel.rb +45 -0
  21. data/lib/action_cable/connection/message_buffer.rb +54 -0
  22. data/lib/action_cable/connection/stream.rb +117 -0
  23. data/lib/action_cable/connection/stream_event_loop.rb +136 -0
  24. data/lib/action_cable/connection/subscriptions.rb +79 -0
  25. data/lib/action_cable/connection/tagged_logger_proxy.rb +42 -0
  26. data/lib/action_cable/connection/test_case.rb +234 -0
  27. data/lib/action_cable/connection/web_socket.rb +41 -0
  28. data/lib/action_cable/engine.rb +79 -0
  29. data/lib/action_cable/gem_version.rb +17 -0
  30. data/lib/action_cable/helpers/action_cable_helper.rb +42 -0
  31. data/lib/action_cable/remote_connections.rb +71 -0
  32. data/lib/action_cable/server.rb +17 -0
  33. data/lib/action_cable/server/base.rb +94 -0
  34. data/lib/action_cable/server/broadcasting.rb +54 -0
  35. data/lib/action_cable/server/configuration.rb +56 -0
  36. data/lib/action_cable/server/connections.rb +36 -0
  37. data/lib/action_cable/server/worker.rb +75 -0
  38. data/lib/action_cable/server/worker/active_record_connection_management.rb +21 -0
  39. data/lib/action_cable/subscription_adapter.rb +12 -0
  40. data/lib/action_cable/subscription_adapter/async.rb +29 -0
  41. data/lib/action_cable/subscription_adapter/base.rb +30 -0
  42. data/lib/action_cable/subscription_adapter/channel_prefix.rb +28 -0
  43. data/lib/action_cable/subscription_adapter/inline.rb +37 -0
  44. data/lib/action_cable/subscription_adapter/postgresql.rb +132 -0
  45. data/lib/action_cable/subscription_adapter/redis.rb +181 -0
  46. data/lib/action_cable/subscription_adapter/subscriber_map.rb +59 -0
  47. data/lib/action_cable/subscription_adapter/test.rb +40 -0
  48. data/lib/action_cable/test_case.rb +11 -0
  49. data/lib/action_cable/test_helper.rb +133 -0
  50. data/lib/action_cable/version.rb +10 -0
  51. data/lib/rails/generators/channel/USAGE +13 -0
  52. data/lib/rails/generators/channel/channel_generator.rb +52 -0
  53. data/lib/rails/generators/channel/templates/application_cable/channel.rb.tt +4 -0
  54. data/lib/rails/generators/channel/templates/application_cable/connection.rb.tt +4 -0
  55. data/lib/rails/generators/channel/templates/channel.rb.tt +16 -0
  56. data/lib/rails/generators/channel/templates/javascript/channel.js.tt +20 -0
  57. data/lib/rails/generators/channel/templates/javascript/consumer.js.tt +6 -0
  58. data/lib/rails/generators/channel/templates/javascript/index.js.tt +5 -0
  59. data/lib/rails/generators/test_unit/channel_generator.rb +20 -0
  60. data/lib/rails/generators/test_unit/templates/channel_test.rb.tt +8 -0
  61. metadata +149 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1b35ec2f5b6c87d54cc2853acf34fdcfae22853878fe1a14d05833e012dd2763
4
+ data.tar.gz: b17c630778a5c4e798638917c4c36ab1a0398280d7292ba56dd1609f890a9350
5
+ SHA512:
6
+ metadata.gz: 9510a0834cb73ea5604a9a37ac37bc9db575467b25a223f83664e343400e3acdb5854cf8b7177f87956a4e2529e0a2ffcbb73882609d28be4d4908616205d24a
7
+ data.tar.gz: 99f05f4254f94d4965e834573d8b6788491f0977654da67a1d90fa787407a190040ab82533f3585acb7796301e502f295b750e29dd1f80870548eea6d843a352
@@ -0,0 +1,169 @@
1
+ ## Rails 6.0.0 (August 16, 2019) ##
2
+
3
+ * No changes.
4
+
5
+
6
+ ## Rails 6.0.0.rc2 (July 22, 2019) ##
7
+
8
+ * No changes.
9
+
10
+
11
+ ## Rails 6.0.0.rc1 (April 24, 2019) ##
12
+
13
+ * No changes.
14
+
15
+
16
+ ## Rails 6.0.0.beta3 (March 11, 2019) ##
17
+
18
+ * No changes.
19
+
20
+
21
+ ## Rails 6.0.0.beta2 (February 25, 2019) ##
22
+
23
+ * PostgreSQL subscription adapters now support `channel_prefix` option in cable.yml
24
+
25
+ Avoids channel name collisions when multiple apps use the same database for Action Cable.
26
+
27
+ *Vladimir Dementyev*
28
+
29
+ * Allow passing custom configuration to `ActionCable::Server::Base`.
30
+
31
+ You can now create a standalone Action Cable server with a custom configuration
32
+ (e.g. to run it in isolation from the default one):
33
+
34
+ ```ruby
35
+ config = ActionCable::Server::Configuration.new
36
+ config.cable = { adapter: "redis", channel_prefix: "custom_" }
37
+
38
+ CUSTOM_CABLE = ActionCable::Server::Base.new(config: config)
39
+ ```
40
+
41
+ Then you can mount it in the `routes.rb` file:
42
+
43
+ ```ruby
44
+ Rails.application.routes.draw do
45
+ mount CUSTOM_CABLE => "/custom_cable"
46
+ # ...
47
+ end
48
+ ```
49
+
50
+ *Vladimir Dementyev*
51
+
52
+ * Add `:action_cable_connection` and `:action_cable_channel` load hooks.
53
+
54
+ You can use them to extend `ActionCable::Connection::Base` and `ActionCable::Channel::Base`
55
+ functionality:
56
+
57
+ ```ruby
58
+ ActiveSupport.on_load(:action_cable_channel) do
59
+ # do something in the context of ActionCable::Channel::Base
60
+ end
61
+ ```
62
+
63
+ *Vladimir Dementyev*
64
+
65
+ * Add `Channel::Base#broadcast_to`.
66
+
67
+ You can now call `broadcast_to` within a channel action, which equals to
68
+ the `self.class.broadcast_to`.
69
+
70
+ *Vladimir Dementyev*
71
+
72
+ * Make `Channel::Base.broadcasting_for` a public API.
73
+
74
+ You can use `.broadcasting_for` to generate a unique stream identifier within
75
+ a channel for the specified target (e.g. Active Record model):
76
+
77
+ ```ruby
78
+ ChatChannel.broadcasting_for(model) # => "chat:<model.to_gid_param>"
79
+ ```
80
+
81
+ *Vladimir Dementyev*
82
+
83
+
84
+ ## Rails 6.0.0.beta1 (January 18, 2019) ##
85
+
86
+ * [Rename npm package](https://github.com/rails/rails/pull/34905) from
87
+ [`actioncable`](https://www.npmjs.com/package/actioncable) to
88
+ [`@rails/actioncable`](https://www.npmjs.com/package/@rails/actioncable).
89
+
90
+ *Javan Makhmali*
91
+
92
+ * Merge [`action-cable-testing`](https://github.com/palkan/action-cable-testing) to Rails.
93
+
94
+ *Vladimir Dementyev*
95
+
96
+ * The JavaScript WebSocket client will no longer try to reconnect
97
+ when you call `reject_unauthorized_connection` on the connection.
98
+
99
+ *Mick Staugaard*
100
+
101
+ * `ActionCable.Connection#getState` now references the configurable
102
+ `ActionCable.adapters.WebSocket` property rather than the `WebSocket` global
103
+ variable, matching the behavior of `ActionCable.Connection#open`.
104
+
105
+ *Richard Macklin*
106
+
107
+ * The ActionCable javascript package has been converted from CoffeeScript
108
+ to ES2015, and we now publish the source code in the npm distribution.
109
+
110
+ This allows ActionCable users to depend on the javascript source code
111
+ rather than the compiled code, which can produce smaller javascript bundles.
112
+
113
+ This change includes some breaking changes to optional parts of the
114
+ ActionCable javascript API:
115
+
116
+ - Configuration of the WebSocket adapter and logger adapter have been moved
117
+ from properties of `ActionCable` to properties of `ActionCable.adapters`.
118
+ If you are currently configuring these adapters you will need to make
119
+ these changes when upgrading:
120
+
121
+ ```diff
122
+ - ActionCable.WebSocket = MyWebSocket
123
+ + ActionCable.adapters.WebSocket = MyWebSocket
124
+ ```
125
+ ```diff
126
+ - ActionCable.logger = myLogger
127
+ + ActionCable.adapters.logger = myLogger
128
+ ```
129
+
130
+ - The `ActionCable.startDebugging()` and `ActionCable.stopDebugging()`
131
+ methods have been removed and replaced with the property
132
+ `ActionCable.logger.enabled`. If you are currently using these methods you
133
+ will need to make these changes when upgrading:
134
+
135
+ ```diff
136
+ - ActionCable.startDebugging()
137
+ + ActionCable.logger.enabled = true
138
+ ```
139
+ ```diff
140
+ - ActionCable.stopDebugging()
141
+ + ActionCable.logger.enabled = false
142
+ ```
143
+
144
+ *Richard Macklin*
145
+
146
+ * Add `id` option to redis adapter so now you can distinguish
147
+ ActionCable's redis connections among others. Also, you can set
148
+ custom id in options.
149
+
150
+ Before:
151
+ ```
152
+ $ redis-cli client list
153
+ id=669 addr=127.0.0.1:46442 fd=8 name= age=18 ...
154
+ ```
155
+
156
+ After:
157
+ ```
158
+ $ redis-cli client list
159
+ id=673 addr=127.0.0.1:46516 fd=8 name=ActionCable-PID-19413 age=2 ...
160
+ ```
161
+
162
+ *Ilia Kasianenko*
163
+
164
+ * Rails 6 requires Ruby 2.5.0 or newer.
165
+
166
+ *Jeremy Daer*, *Kasper Timm Hansen*
167
+
168
+
169
+ Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/actioncable/CHANGELOG.md) for previous changes.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2015-2019 Basecamp, LLC
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,24 @@
1
+ # Action Cable – Integrated WebSockets for Rails
2
+
3
+ Action Cable seamlessly integrates WebSockets with the rest of your Rails application.
4
+ It allows for real-time features to be written in Ruby in the same style
5
+ and form as the rest of your Rails application, while still being performant
6
+ and scalable. It's a full-stack offering that provides both a client-side
7
+ JavaScript framework and a server-side Ruby framework. You have access to your full
8
+ domain model written with Active Record or your ORM of choice.
9
+
10
+ You can read more about Action Cable in the [Action Cable Overview](https://edgeguides.rubyonrails.org/action_cable_overview.html) guide.
11
+
12
+ ## Support
13
+
14
+ API documentation is at:
15
+
16
+ * https://api.rubyonrails.org
17
+
18
+ Bug reports for the Ruby on Rails project can be filed here:
19
+
20
+ * https://github.com/rails/rails/issues
21
+
22
+ Feature requests should be discussed on the rails-core mailing list here:
23
+
24
+ * https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
@@ -0,0 +1,517 @@
1
+ (function(global, factory) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define([ "exports" ], factory) : factory(global.ActionCable = {});
3
+ })(this, function(exports) {
4
+ "use strict";
5
+ var adapters = {
6
+ logger: self.console,
7
+ WebSocket: self.WebSocket
8
+ };
9
+ var logger = {
10
+ log: function log() {
11
+ if (this.enabled) {
12
+ var _adapters$logger;
13
+ for (var _len = arguments.length, messages = Array(_len), _key = 0; _key < _len; _key++) {
14
+ messages[_key] = arguments[_key];
15
+ }
16
+ messages.push(Date.now());
17
+ (_adapters$logger = adapters.logger).log.apply(_adapters$logger, [ "[ActionCable]" ].concat(messages));
18
+ }
19
+ }
20
+ };
21
+ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function(obj) {
22
+ return typeof obj;
23
+ } : function(obj) {
24
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
25
+ };
26
+ var classCallCheck = function(instance, Constructor) {
27
+ if (!(instance instanceof Constructor)) {
28
+ throw new TypeError("Cannot call a class as a function");
29
+ }
30
+ };
31
+ var createClass = function() {
32
+ function defineProperties(target, props) {
33
+ for (var i = 0; i < props.length; i++) {
34
+ var descriptor = props[i];
35
+ descriptor.enumerable = descriptor.enumerable || false;
36
+ descriptor.configurable = true;
37
+ if ("value" in descriptor) descriptor.writable = true;
38
+ Object.defineProperty(target, descriptor.key, descriptor);
39
+ }
40
+ }
41
+ return function(Constructor, protoProps, staticProps) {
42
+ if (protoProps) defineProperties(Constructor.prototype, protoProps);
43
+ if (staticProps) defineProperties(Constructor, staticProps);
44
+ return Constructor;
45
+ };
46
+ }();
47
+ var now = function now() {
48
+ return new Date().getTime();
49
+ };
50
+ var secondsSince = function secondsSince(time) {
51
+ return (now() - time) / 1e3;
52
+ };
53
+ var clamp = function clamp(number, min, max) {
54
+ return Math.max(min, Math.min(max, number));
55
+ };
56
+ var ConnectionMonitor = function() {
57
+ function ConnectionMonitor(connection) {
58
+ classCallCheck(this, ConnectionMonitor);
59
+ this.visibilityDidChange = this.visibilityDidChange.bind(this);
60
+ this.connection = connection;
61
+ this.reconnectAttempts = 0;
62
+ }
63
+ ConnectionMonitor.prototype.start = function start() {
64
+ if (!this.isRunning()) {
65
+ this.startedAt = now();
66
+ delete this.stoppedAt;
67
+ this.startPolling();
68
+ addEventListener("visibilitychange", this.visibilityDidChange);
69
+ logger.log("ConnectionMonitor started. pollInterval = " + this.getPollInterval() + " ms");
70
+ }
71
+ };
72
+ ConnectionMonitor.prototype.stop = function stop() {
73
+ if (this.isRunning()) {
74
+ this.stoppedAt = now();
75
+ this.stopPolling();
76
+ removeEventListener("visibilitychange", this.visibilityDidChange);
77
+ logger.log("ConnectionMonitor stopped");
78
+ }
79
+ };
80
+ ConnectionMonitor.prototype.isRunning = function isRunning() {
81
+ return this.startedAt && !this.stoppedAt;
82
+ };
83
+ ConnectionMonitor.prototype.recordPing = function recordPing() {
84
+ this.pingedAt = now();
85
+ };
86
+ ConnectionMonitor.prototype.recordConnect = function recordConnect() {
87
+ this.reconnectAttempts = 0;
88
+ this.recordPing();
89
+ delete this.disconnectedAt;
90
+ logger.log("ConnectionMonitor recorded connect");
91
+ };
92
+ ConnectionMonitor.prototype.recordDisconnect = function recordDisconnect() {
93
+ this.disconnectedAt = now();
94
+ logger.log("ConnectionMonitor recorded disconnect");
95
+ };
96
+ ConnectionMonitor.prototype.startPolling = function startPolling() {
97
+ this.stopPolling();
98
+ this.poll();
99
+ };
100
+ ConnectionMonitor.prototype.stopPolling = function stopPolling() {
101
+ clearTimeout(this.pollTimeout);
102
+ };
103
+ ConnectionMonitor.prototype.poll = function poll() {
104
+ var _this = this;
105
+ this.pollTimeout = setTimeout(function() {
106
+ _this.reconnectIfStale();
107
+ _this.poll();
108
+ }, this.getPollInterval());
109
+ };
110
+ ConnectionMonitor.prototype.getPollInterval = function getPollInterval() {
111
+ var _constructor$pollInte = this.constructor.pollInterval, min = _constructor$pollInte.min, max = _constructor$pollInte.max, multiplier = _constructor$pollInte.multiplier;
112
+ var interval = multiplier * Math.log(this.reconnectAttempts + 1);
113
+ return Math.round(clamp(interval, min, max) * 1e3);
114
+ };
115
+ ConnectionMonitor.prototype.reconnectIfStale = function reconnectIfStale() {
116
+ if (this.connectionIsStale()) {
117
+ logger.log("ConnectionMonitor detected stale connection. reconnectAttempts = " + this.reconnectAttempts + ", pollInterval = " + this.getPollInterval() + " ms, time disconnected = " + secondsSince(this.disconnectedAt) + " s, stale threshold = " + this.constructor.staleThreshold + " s");
118
+ this.reconnectAttempts++;
119
+ if (this.disconnectedRecently()) {
120
+ logger.log("ConnectionMonitor skipping reopening recent disconnect");
121
+ } else {
122
+ logger.log("ConnectionMonitor reopening");
123
+ this.connection.reopen();
124
+ }
125
+ }
126
+ };
127
+ ConnectionMonitor.prototype.connectionIsStale = function connectionIsStale() {
128
+ return secondsSince(this.pingedAt ? this.pingedAt : this.startedAt) > this.constructor.staleThreshold;
129
+ };
130
+ ConnectionMonitor.prototype.disconnectedRecently = function disconnectedRecently() {
131
+ return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold;
132
+ };
133
+ ConnectionMonitor.prototype.visibilityDidChange = function visibilityDidChange() {
134
+ var _this2 = this;
135
+ if (document.visibilityState === "visible") {
136
+ setTimeout(function() {
137
+ if (_this2.connectionIsStale() || !_this2.connection.isOpen()) {
138
+ logger.log("ConnectionMonitor reopening stale connection on visibilitychange. visbilityState = " + document.visibilityState);
139
+ _this2.connection.reopen();
140
+ }
141
+ }, 200);
142
+ }
143
+ };
144
+ return ConnectionMonitor;
145
+ }();
146
+ ConnectionMonitor.pollInterval = {
147
+ min: 3,
148
+ max: 30,
149
+ multiplier: 5
150
+ };
151
+ ConnectionMonitor.staleThreshold = 6;
152
+ var INTERNAL = {
153
+ message_types: {
154
+ welcome: "welcome",
155
+ disconnect: "disconnect",
156
+ ping: "ping",
157
+ confirmation: "confirm_subscription",
158
+ rejection: "reject_subscription"
159
+ },
160
+ disconnect_reasons: {
161
+ unauthorized: "unauthorized",
162
+ invalid_request: "invalid_request",
163
+ server_restart: "server_restart"
164
+ },
165
+ default_mount_path: "/cable",
166
+ protocols: [ "actioncable-v1-json", "actioncable-unsupported" ]
167
+ };
168
+ var message_types = INTERNAL.message_types, protocols = INTERNAL.protocols;
169
+ var supportedProtocols = protocols.slice(0, protocols.length - 1);
170
+ var indexOf = [].indexOf;
171
+ var Connection = function() {
172
+ function Connection(consumer) {
173
+ classCallCheck(this, Connection);
174
+ this.open = this.open.bind(this);
175
+ this.consumer = consumer;
176
+ this.subscriptions = this.consumer.subscriptions;
177
+ this.monitor = new ConnectionMonitor(this);
178
+ this.disconnected = true;
179
+ }
180
+ Connection.prototype.send = function send(data) {
181
+ if (this.isOpen()) {
182
+ this.webSocket.send(JSON.stringify(data));
183
+ return true;
184
+ } else {
185
+ return false;
186
+ }
187
+ };
188
+ Connection.prototype.open = function open() {
189
+ if (this.isActive()) {
190
+ logger.log("Attempted to open WebSocket, but existing socket is " + this.getState());
191
+ return false;
192
+ } else {
193
+ logger.log("Opening WebSocket, current state is " + this.getState() + ", subprotocols: " + protocols);
194
+ if (this.webSocket) {
195
+ this.uninstallEventHandlers();
196
+ }
197
+ this.webSocket = new adapters.WebSocket(this.consumer.url, protocols);
198
+ this.installEventHandlers();
199
+ this.monitor.start();
200
+ return true;
201
+ }
202
+ };
203
+ Connection.prototype.close = function close() {
204
+ var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
205
+ allowReconnect: true
206
+ }, allowReconnect = _ref.allowReconnect;
207
+ if (!allowReconnect) {
208
+ this.monitor.stop();
209
+ }
210
+ if (this.isActive()) {
211
+ return this.webSocket.close();
212
+ }
213
+ };
214
+ Connection.prototype.reopen = function reopen() {
215
+ logger.log("Reopening WebSocket, current state is " + this.getState());
216
+ if (this.isActive()) {
217
+ try {
218
+ return this.close();
219
+ } catch (error) {
220
+ logger.log("Failed to reopen WebSocket", error);
221
+ } finally {
222
+ logger.log("Reopening WebSocket in " + this.constructor.reopenDelay + "ms");
223
+ setTimeout(this.open, this.constructor.reopenDelay);
224
+ }
225
+ } else {
226
+ return this.open();
227
+ }
228
+ };
229
+ Connection.prototype.getProtocol = function getProtocol() {
230
+ if (this.webSocket) {
231
+ return this.webSocket.protocol;
232
+ }
233
+ };
234
+ Connection.prototype.isOpen = function isOpen() {
235
+ return this.isState("open");
236
+ };
237
+ Connection.prototype.isActive = function isActive() {
238
+ return this.isState("open", "connecting");
239
+ };
240
+ Connection.prototype.isProtocolSupported = function isProtocolSupported() {
241
+ return indexOf.call(supportedProtocols, this.getProtocol()) >= 0;
242
+ };
243
+ Connection.prototype.isState = function isState() {
244
+ for (var _len = arguments.length, states = Array(_len), _key = 0; _key < _len; _key++) {
245
+ states[_key] = arguments[_key];
246
+ }
247
+ return indexOf.call(states, this.getState()) >= 0;
248
+ };
249
+ Connection.prototype.getState = function getState() {
250
+ if (this.webSocket) {
251
+ for (var state in adapters.WebSocket) {
252
+ if (adapters.WebSocket[state] === this.webSocket.readyState) {
253
+ return state.toLowerCase();
254
+ }
255
+ }
256
+ }
257
+ return null;
258
+ };
259
+ Connection.prototype.installEventHandlers = function installEventHandlers() {
260
+ for (var eventName in this.events) {
261
+ var handler = this.events[eventName].bind(this);
262
+ this.webSocket["on" + eventName] = handler;
263
+ }
264
+ };
265
+ Connection.prototype.uninstallEventHandlers = function uninstallEventHandlers() {
266
+ for (var eventName in this.events) {
267
+ this.webSocket["on" + eventName] = function() {};
268
+ }
269
+ };
270
+ return Connection;
271
+ }();
272
+ Connection.reopenDelay = 500;
273
+ Connection.prototype.events = {
274
+ message: function message(event) {
275
+ if (!this.isProtocolSupported()) {
276
+ return;
277
+ }
278
+ var _JSON$parse = JSON.parse(event.data), identifier = _JSON$parse.identifier, message = _JSON$parse.message, reason = _JSON$parse.reason, reconnect = _JSON$parse.reconnect, type = _JSON$parse.type;
279
+ switch (type) {
280
+ case message_types.welcome:
281
+ this.monitor.recordConnect();
282
+ return this.subscriptions.reload();
283
+
284
+ case message_types.disconnect:
285
+ logger.log("Disconnecting. Reason: " + reason);
286
+ return this.close({
287
+ allowReconnect: reconnect
288
+ });
289
+
290
+ case message_types.ping:
291
+ return this.monitor.recordPing();
292
+
293
+ case message_types.confirmation:
294
+ return this.subscriptions.notify(identifier, "connected");
295
+
296
+ case message_types.rejection:
297
+ return this.subscriptions.reject(identifier);
298
+
299
+ default:
300
+ return this.subscriptions.notify(identifier, "received", message);
301
+ }
302
+ },
303
+ open: function open() {
304
+ logger.log("WebSocket onopen event, using '" + this.getProtocol() + "' subprotocol");
305
+ this.disconnected = false;
306
+ if (!this.isProtocolSupported()) {
307
+ logger.log("Protocol is unsupported. Stopping monitor and disconnecting.");
308
+ return this.close({
309
+ allowReconnect: false
310
+ });
311
+ }
312
+ },
313
+ close: function close(event) {
314
+ logger.log("WebSocket onclose event");
315
+ if (this.disconnected) {
316
+ return;
317
+ }
318
+ this.disconnected = true;
319
+ this.monitor.recordDisconnect();
320
+ return this.subscriptions.notifyAll("disconnected", {
321
+ willAttemptReconnect: this.monitor.isRunning()
322
+ });
323
+ },
324
+ error: function error() {
325
+ logger.log("WebSocket onerror event");
326
+ }
327
+ };
328
+ var extend = function extend(object, properties) {
329
+ if (properties != null) {
330
+ for (var key in properties) {
331
+ var value = properties[key];
332
+ object[key] = value;
333
+ }
334
+ }
335
+ return object;
336
+ };
337
+ var Subscription = function() {
338
+ function Subscription(consumer) {
339
+ var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
340
+ var mixin = arguments[2];
341
+ classCallCheck(this, Subscription);
342
+ this.consumer = consumer;
343
+ this.identifier = JSON.stringify(params);
344
+ extend(this, mixin);
345
+ }
346
+ Subscription.prototype.perform = function perform(action) {
347
+ var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
348
+ data.action = action;
349
+ return this.send(data);
350
+ };
351
+ Subscription.prototype.send = function send(data) {
352
+ return this.consumer.send({
353
+ command: "message",
354
+ identifier: this.identifier,
355
+ data: JSON.stringify(data)
356
+ });
357
+ };
358
+ Subscription.prototype.unsubscribe = function unsubscribe() {
359
+ return this.consumer.subscriptions.remove(this);
360
+ };
361
+ return Subscription;
362
+ }();
363
+ var Subscriptions = function() {
364
+ function Subscriptions(consumer) {
365
+ classCallCheck(this, Subscriptions);
366
+ this.consumer = consumer;
367
+ this.subscriptions = [];
368
+ }
369
+ Subscriptions.prototype.create = function create(channelName, mixin) {
370
+ var channel = channelName;
371
+ var params = (typeof channel === "undefined" ? "undefined" : _typeof(channel)) === "object" ? channel : {
372
+ channel: channel
373
+ };
374
+ var subscription = new Subscription(this.consumer, params, mixin);
375
+ return this.add(subscription);
376
+ };
377
+ Subscriptions.prototype.add = function add(subscription) {
378
+ this.subscriptions.push(subscription);
379
+ this.consumer.ensureActiveConnection();
380
+ this.notify(subscription, "initialized");
381
+ this.sendCommand(subscription, "subscribe");
382
+ return subscription;
383
+ };
384
+ Subscriptions.prototype.remove = function remove(subscription) {
385
+ this.forget(subscription);
386
+ if (!this.findAll(subscription.identifier).length) {
387
+ this.sendCommand(subscription, "unsubscribe");
388
+ }
389
+ return subscription;
390
+ };
391
+ Subscriptions.prototype.reject = function reject(identifier) {
392
+ var _this = this;
393
+ return this.findAll(identifier).map(function(subscription) {
394
+ _this.forget(subscription);
395
+ _this.notify(subscription, "rejected");
396
+ return subscription;
397
+ });
398
+ };
399
+ Subscriptions.prototype.forget = function forget(subscription) {
400
+ this.subscriptions = this.subscriptions.filter(function(s) {
401
+ return s !== subscription;
402
+ });
403
+ return subscription;
404
+ };
405
+ Subscriptions.prototype.findAll = function findAll(identifier) {
406
+ return this.subscriptions.filter(function(s) {
407
+ return s.identifier === identifier;
408
+ });
409
+ };
410
+ Subscriptions.prototype.reload = function reload() {
411
+ var _this2 = this;
412
+ return this.subscriptions.map(function(subscription) {
413
+ return _this2.sendCommand(subscription, "subscribe");
414
+ });
415
+ };
416
+ Subscriptions.prototype.notifyAll = function notifyAll(callbackName) {
417
+ var _this3 = this;
418
+ for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
419
+ args[_key - 1] = arguments[_key];
420
+ }
421
+ return this.subscriptions.map(function(subscription) {
422
+ return _this3.notify.apply(_this3, [ subscription, callbackName ].concat(args));
423
+ });
424
+ };
425
+ Subscriptions.prototype.notify = function notify(subscription, callbackName) {
426
+ for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
427
+ args[_key2 - 2] = arguments[_key2];
428
+ }
429
+ var subscriptions = void 0;
430
+ if (typeof subscription === "string") {
431
+ subscriptions = this.findAll(subscription);
432
+ } else {
433
+ subscriptions = [ subscription ];
434
+ }
435
+ return subscriptions.map(function(subscription) {
436
+ return typeof subscription[callbackName] === "function" ? subscription[callbackName].apply(subscription, args) : undefined;
437
+ });
438
+ };
439
+ Subscriptions.prototype.sendCommand = function sendCommand(subscription, command) {
440
+ var identifier = subscription.identifier;
441
+ return this.consumer.send({
442
+ command: command,
443
+ identifier: identifier
444
+ });
445
+ };
446
+ return Subscriptions;
447
+ }();
448
+ var Consumer = function() {
449
+ function Consumer(url) {
450
+ classCallCheck(this, Consumer);
451
+ this._url = url;
452
+ this.subscriptions = new Subscriptions(this);
453
+ this.connection = new Connection(this);
454
+ }
455
+ Consumer.prototype.send = function send(data) {
456
+ return this.connection.send(data);
457
+ };
458
+ Consumer.prototype.connect = function connect() {
459
+ return this.connection.open();
460
+ };
461
+ Consumer.prototype.disconnect = function disconnect() {
462
+ return this.connection.close({
463
+ allowReconnect: false
464
+ });
465
+ };
466
+ Consumer.prototype.ensureActiveConnection = function ensureActiveConnection() {
467
+ if (!this.connection.isActive()) {
468
+ return this.connection.open();
469
+ }
470
+ };
471
+ createClass(Consumer, [ {
472
+ key: "url",
473
+ get: function get$$1() {
474
+ return createWebSocketURL(this._url);
475
+ }
476
+ } ]);
477
+ return Consumer;
478
+ }();
479
+ function createWebSocketURL(url) {
480
+ if (typeof url === "function") {
481
+ url = url();
482
+ }
483
+ if (url && !/^wss?:/i.test(url)) {
484
+ var a = document.createElement("a");
485
+ a.href = url;
486
+ a.href = a.href;
487
+ a.protocol = a.protocol.replace("http", "ws");
488
+ return a.href;
489
+ } else {
490
+ return url;
491
+ }
492
+ }
493
+ function createConsumer() {
494
+ var url = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getConfig("url") || INTERNAL.default_mount_path;
495
+ return new Consumer(url);
496
+ }
497
+ function getConfig(name) {
498
+ var element = document.head.querySelector("meta[name='action-cable-" + name + "']");
499
+ if (element) {
500
+ return element.getAttribute("content");
501
+ }
502
+ }
503
+ exports.Connection = Connection;
504
+ exports.ConnectionMonitor = ConnectionMonitor;
505
+ exports.Consumer = Consumer;
506
+ exports.INTERNAL = INTERNAL;
507
+ exports.Subscription = Subscription;
508
+ exports.Subscriptions = Subscriptions;
509
+ exports.adapters = adapters;
510
+ exports.createWebSocketURL = createWebSocketURL;
511
+ exports.logger = logger;
512
+ exports.createConsumer = createConsumer;
513
+ exports.getConfig = getConfig;
514
+ Object.defineProperty(exports, "__esModule", {
515
+ value: true
516
+ });
517
+ });