actioncable 6.0.0

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 +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
+ });