aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--actioncable/.gitignore1
-rw-r--r--actioncable/CHANGELOG.md39
-rw-r--r--actioncable/app/assets/javascripts/action_cable.js403
-rw-r--r--actioncable/app/javascript/action_cable/adapters.js4
-rw-r--r--actioncable/app/javascript/action_cable/connection.js26
-rw-r--r--actioncable/app/javascript/action_cable/connection_monitor.js18
-rw-r--r--actioncable/app/javascript/action_cable/consumer.js7
-rw-r--r--actioncable/app/javascript/action_cable/index.js67
-rw-r--r--actioncable/app/javascript/action_cable/logger.js10
-rw-r--r--actioncable/app/javascript/action_cable/subscriptions.js4
-rw-r--r--actioncable/package.json4
-rw-r--r--actioncable/rollup.config.js4
-rw-r--r--actioncable/test/javascript/src/test_helpers/consumer_test_helper.js4
-rw-r--r--actioncable/test/javascript/src/test_helpers/index.js6
-rw-r--r--actioncable/test/javascript/src/unit/action_cable_test.js16
16 files changed, 325 insertions, 290 deletions
diff --git a/.travis.yml b/.travis.yml
index 475859b1d7..9097edbfdd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -37,7 +37,7 @@ before_install:
- "travis_retry gem update --system"
- "travis_retry gem install bundler"
- "[[ -z $encrypted_0fb9444d0374_key && -z $encrypted_0fb9444d0374_iv ]] || openssl aes-256-cbc -K $encrypted_0fb9444d0374_key -iv $encrypted_0fb9444d0374_iv -in activestorage/test/service/configurations.yml.enc -out activestorage/test/service/configurations.yml -d"
- - "[[ $GEM != 'ac:integration' ]] || (cd actioncable && yarn install)"
+ - "[[ $GEM != 'ac:integration' ]] || yarn install"
- "[[ $GEM != 'av:ujs' ]] || nvm install node"
- "[[ $GEM != 'av:ujs' ]] || node --version"
- "[[ $GEM != 'av:ujs' ]] || (cd actionview && npm install)"
diff --git a/actioncable/.gitignore b/actioncable/.gitignore
index 53c5720e8c..7fa7c03e03 100644
--- a/actioncable/.gitignore
+++ b/actioncable/.gitignore
@@ -1,3 +1,4 @@
/app/javascript/action_cable/internal.js
+/src
/test/javascript/compiled/
/tmp/
diff --git a/actioncable/CHANGELOG.md b/actioncable/CHANGELOG.md
index 17c7e9a3b7..c0ab0485f3 100644
--- a/actioncable/CHANGELOG.md
+++ b/actioncable/CHANGELOG.md
@@ -1,3 +1,42 @@
+* The ActionCable javascript package has been converted from CoffeeScript
+ to ES2015, and we now publish the source code in the npm distribution.
+
+ This allows ActionCable users to depend on the javascript source code
+ rather than the compiled code, which can produce smaller javascript bundles.
+
+ This change includes some breaking changes to optional parts of the
+ ActionCable javascript API:
+
+ - Configuration of the WebSocket adapter and logger adapter have been moved
+ from properties of `ActionCable` to properties of `ActionCable.adapters`.
+ If you are currently configuring these adapters you will need to make
+ these changes when upgrading:
+
+ ```diff
+ - ActionCable.WebSocket = MyWebSocket
+ + ActionCable.adapters.WebSocket = MyWebSocket
+ ```
+ ```diff
+ - ActionCable.logger = myLogger
+ + ActionCable.adapters.logger = myLogger
+ ```
+
+ - The `ActionCable.startDebugging()` and `ActionCable.stopDebugging()`
+ methods have been removed and replaced with the property
+ `ActionCable.logger.enabled`. If you are currently using these methods you
+ will need to make these changes when upgrading:
+
+ ```diff
+ - ActionCable.startDebugging()
+ + ActionCable.logger.enabled = true
+ ```
+ ```diff
+ - ActionCable.stopDebugging()
+ + ActionCable.logger.enabled = false
+ ```
+
+ *Richard Macklin*
+
* Add `id` option to redis adapter so now you can distinguish
ActionCable's redis connections among others. Also, you can set
custom id in options.
diff --git a/actioncable/app/assets/javascripts/action_cable.js b/actioncable/app/assets/javascripts/action_cable.js
index 07151b9d25..90e8e6b99b 100644
--- a/actioncable/app/assets/javascripts/action_cable.js
+++ b/actioncable/app/assets/javascripts/action_cable.js
@@ -1,16 +1,22 @@
(function(global, factory) {
- typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define(factory) : global.ActionCable = factory();
-})(this, function() {
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define([ "exports" ], factory) : factory(global.ActionCable = {});
+})(this, function(exports) {
"use strict";
- var INTERNAL = {
- message_types: {
- welcome: "welcome",
- ping: "ping",
- confirmation: "confirm_subscription",
- rejection: "reject_subscription"
- },
- default_mount_path: "/cable",
- protocols: [ "actioncable-v1-json", "actioncable-unsupported" ]
+ var adapters = {
+ logger: window.console,
+ WebSocket: window.WebSocket
+ };
+ var logger = {
+ log: function log() {
+ if (this.enabled) {
+ var _adapters$logger;
+ for (var _len = arguments.length, messages = Array(_len), _key = 0; _key < _len; _key++) {
+ messages[_key] = arguments[_key];
+ }
+ messages.push(Date.now());
+ (_adapters$logger = adapters.logger).log.apply(_adapters$logger, [ "[ActionCable]" ].concat(messages));
+ }
+ }
};
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function(obj) {
return typeof obj;
@@ -22,6 +28,121 @@
throw new TypeError("Cannot call a class as a function");
}
};
+ var now = function now() {
+ return new Date().getTime();
+ };
+ var secondsSince = function secondsSince(time) {
+ return (now() - time) / 1e3;
+ };
+ var clamp = function clamp(number, min, max) {
+ return Math.max(min, Math.min(max, number));
+ };
+ var ConnectionMonitor = function() {
+ function ConnectionMonitor(connection) {
+ classCallCheck(this, ConnectionMonitor);
+ this.visibilityDidChange = this.visibilityDidChange.bind(this);
+ this.connection = connection;
+ this.reconnectAttempts = 0;
+ }
+ ConnectionMonitor.prototype.start = function start() {
+ if (!this.isRunning()) {
+ this.startedAt = now();
+ delete this.stoppedAt;
+ this.startPolling();
+ document.addEventListener("visibilitychange", this.visibilityDidChange);
+ logger.log("ConnectionMonitor started. pollInterval = " + this.getPollInterval() + " ms");
+ }
+ };
+ ConnectionMonitor.prototype.stop = function stop() {
+ if (this.isRunning()) {
+ this.stoppedAt = now();
+ this.stopPolling();
+ document.removeEventListener("visibilitychange", this.visibilityDidChange);
+ logger.log("ConnectionMonitor stopped");
+ }
+ };
+ ConnectionMonitor.prototype.isRunning = function isRunning() {
+ return this.startedAt && !this.stoppedAt;
+ };
+ ConnectionMonitor.prototype.recordPing = function recordPing() {
+ this.pingedAt = now();
+ };
+ ConnectionMonitor.prototype.recordConnect = function recordConnect() {
+ this.reconnectAttempts = 0;
+ this.recordPing();
+ delete this.disconnectedAt;
+ logger.log("ConnectionMonitor recorded connect");
+ };
+ ConnectionMonitor.prototype.recordDisconnect = function recordDisconnect() {
+ this.disconnectedAt = now();
+ logger.log("ConnectionMonitor recorded disconnect");
+ };
+ ConnectionMonitor.prototype.startPolling = function startPolling() {
+ this.stopPolling();
+ this.poll();
+ };
+ ConnectionMonitor.prototype.stopPolling = function stopPolling() {
+ clearTimeout(this.pollTimeout);
+ };
+ ConnectionMonitor.prototype.poll = function poll() {
+ var _this = this;
+ this.pollTimeout = setTimeout(function() {
+ _this.reconnectIfStale();
+ _this.poll();
+ }, this.getPollInterval());
+ };
+ ConnectionMonitor.prototype.getPollInterval = function getPollInterval() {
+ var _constructor$pollInte = this.constructor.pollInterval, min = _constructor$pollInte.min, max = _constructor$pollInte.max, multiplier = _constructor$pollInte.multiplier;
+ var interval = multiplier * Math.log(this.reconnectAttempts + 1);
+ return Math.round(clamp(interval, min, max) * 1e3);
+ };
+ ConnectionMonitor.prototype.reconnectIfStale = function reconnectIfStale() {
+ if (this.connectionIsStale()) {
+ 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");
+ this.reconnectAttempts++;
+ if (this.disconnectedRecently()) {
+ logger.log("ConnectionMonitor skipping reopening recent disconnect");
+ } else {
+ logger.log("ConnectionMonitor reopening");
+ this.connection.reopen();
+ }
+ }
+ };
+ ConnectionMonitor.prototype.connectionIsStale = function connectionIsStale() {
+ return secondsSince(this.pingedAt ? this.pingedAt : this.startedAt) > this.constructor.staleThreshold;
+ };
+ ConnectionMonitor.prototype.disconnectedRecently = function disconnectedRecently() {
+ return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold;
+ };
+ ConnectionMonitor.prototype.visibilityDidChange = function visibilityDidChange() {
+ var _this2 = this;
+ if (document.visibilityState === "visible") {
+ setTimeout(function() {
+ if (_this2.connectionIsStale() || !_this2.connection.isOpen()) {
+ logger.log("ConnectionMonitor reopening stale connection on visibilitychange. visbilityState = " + document.visibilityState);
+ _this2.connection.reopen();
+ }
+ }, 200);
+ }
+ };
+ return ConnectionMonitor;
+ }();
+ ConnectionMonitor.pollInterval = {
+ min: 3,
+ max: 30,
+ multiplier: 5
+ };
+ ConnectionMonitor.staleThreshold = 6;
+ var INTERNAL = {
+ message_types: {
+ welcome: "welcome",
+ ping: "ping",
+ confirmation: "confirm_subscription",
+ rejection: "reject_subscription"
+ },
+ default_mount_path: "/cable",
+ protocols: [ "actioncable-v1-json", "actioncable-unsupported" ]
+ };
var message_types = INTERNAL.message_types, protocols = INTERNAL.protocols;
var supportedProtocols = protocols.slice(0, protocols.length - 1);
var indexOf = [].indexOf;
@@ -31,7 +152,7 @@
this.open = this.open.bind(this);
this.consumer = consumer;
this.subscriptions = this.consumer.subscriptions;
- this.monitor = new ActionCable.ConnectionMonitor(this);
+ this.monitor = new ConnectionMonitor(this);
this.disconnected = true;
}
Connection.prototype.send = function send(data) {
@@ -44,14 +165,14 @@
};
Connection.prototype.open = function open() {
if (this.isActive()) {
- ActionCable.log("Attempted to open WebSocket, but existing socket is " + this.getState());
+ logger.log("Attempted to open WebSocket, but existing socket is " + this.getState());
return false;
} else {
- ActionCable.log("Opening WebSocket, current state is " + this.getState() + ", subprotocols: " + protocols);
+ logger.log("Opening WebSocket, current state is " + this.getState() + ", subprotocols: " + protocols);
if (this.webSocket) {
this.uninstallEventHandlers();
}
- this.webSocket = new ActionCable.WebSocket(this.consumer.url, protocols);
+ this.webSocket = new adapters.WebSocket(this.consumer.url, protocols);
this.installEventHandlers();
this.monitor.start();
return true;
@@ -69,14 +190,14 @@
}
};
Connection.prototype.reopen = function reopen() {
- ActionCable.log("Reopening WebSocket, current state is " + this.getState());
+ logger.log("Reopening WebSocket, current state is " + this.getState());
if (this.isActive()) {
try {
return this.close();
} catch (error) {
- ActionCable.log("Failed to reopen WebSocket", error);
+ logger.log("Failed to reopen WebSocket", error);
} finally {
- ActionCable.log("Reopening WebSocket in " + this.constructor.reopenDelay + "ms");
+ logger.log("Reopening WebSocket in " + this.constructor.reopenDelay + "ms");
setTimeout(this.open, this.constructor.reopenDelay);
}
} else {
@@ -150,17 +271,17 @@
}
},
open: function open() {
- ActionCable.log("WebSocket onopen event, using '" + this.getProtocol() + "' subprotocol");
+ logger.log("WebSocket onopen event, using '" + this.getProtocol() + "' subprotocol");
this.disconnected = false;
if (!this.isProtocolSupported()) {
- ActionCable.log("Protocol is unsupported. Stopping monitor and disconnecting.");
+ logger.log("Protocol is unsupported. Stopping monitor and disconnecting.");
return this.close({
allowReconnect: false
});
}
},
close: function close(event) {
- ActionCable.log("WebSocket onclose event");
+ logger.log("WebSocket onclose event");
if (this.disconnected) {
return;
}
@@ -171,139 +292,9 @@
});
},
error: function error() {
- ActionCable.log("WebSocket onerror event");
+ logger.log("WebSocket onerror event");
}
};
- var now = function now() {
- return new Date().getTime();
- };
- var secondsSince = function secondsSince(time) {
- return (now() - time) / 1e3;
- };
- var clamp = function clamp(number, min, max) {
- return Math.max(min, Math.min(max, number));
- };
- var ConnectionMonitor = function() {
- function ConnectionMonitor(connection) {
- classCallCheck(this, ConnectionMonitor);
- this.visibilityDidChange = this.visibilityDidChange.bind(this);
- this.connection = connection;
- this.reconnectAttempts = 0;
- }
- ConnectionMonitor.prototype.start = function start() {
- if (!this.isRunning()) {
- this.startedAt = now();
- delete this.stoppedAt;
- this.startPolling();
- document.addEventListener("visibilitychange", this.visibilityDidChange);
- ActionCable.log("ConnectionMonitor started. pollInterval = " + this.getPollInterval() + " ms");
- }
- };
- ConnectionMonitor.prototype.stop = function stop() {
- if (this.isRunning()) {
- this.stoppedAt = now();
- this.stopPolling();
- document.removeEventListener("visibilitychange", this.visibilityDidChange);
- ActionCable.log("ConnectionMonitor stopped");
- }
- };
- ConnectionMonitor.prototype.isRunning = function isRunning() {
- return this.startedAt && !this.stoppedAt;
- };
- ConnectionMonitor.prototype.recordPing = function recordPing() {
- this.pingedAt = now();
- };
- ConnectionMonitor.prototype.recordConnect = function recordConnect() {
- this.reconnectAttempts = 0;
- this.recordPing();
- delete this.disconnectedAt;
- ActionCable.log("ConnectionMonitor recorded connect");
- };
- ConnectionMonitor.prototype.recordDisconnect = function recordDisconnect() {
- this.disconnectedAt = now();
- ActionCable.log("ConnectionMonitor recorded disconnect");
- };
- ConnectionMonitor.prototype.startPolling = function startPolling() {
- this.stopPolling();
- this.poll();
- };
- ConnectionMonitor.prototype.stopPolling = function stopPolling() {
- clearTimeout(this.pollTimeout);
- };
- ConnectionMonitor.prototype.poll = function poll() {
- var _this = this;
- this.pollTimeout = setTimeout(function() {
- _this.reconnectIfStale();
- _this.poll();
- }, this.getPollInterval());
- };
- ConnectionMonitor.prototype.getPollInterval = function getPollInterval() {
- var _constructor$pollInte = this.constructor.pollInterval, min = _constructor$pollInte.min, max = _constructor$pollInte.max, multiplier = _constructor$pollInte.multiplier;
- var interval = multiplier * Math.log(this.reconnectAttempts + 1);
- return Math.round(clamp(interval, min, max) * 1e3);
- };
- ConnectionMonitor.prototype.reconnectIfStale = function reconnectIfStale() {
- if (this.connectionIsStale()) {
- 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");
- this.reconnectAttempts++;
- if (this.disconnectedRecently()) {
- ActionCable.log("ConnectionMonitor skipping reopening recent disconnect");
- } else {
- ActionCable.log("ConnectionMonitor reopening");
- this.connection.reopen();
- }
- }
- };
- ConnectionMonitor.prototype.connectionIsStale = function connectionIsStale() {
- return secondsSince(this.pingedAt ? this.pingedAt : this.startedAt) > this.constructor.staleThreshold;
- };
- ConnectionMonitor.prototype.disconnectedRecently = function disconnectedRecently() {
- return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold;
- };
- ConnectionMonitor.prototype.visibilityDidChange = function visibilityDidChange() {
- var _this2 = this;
- if (document.visibilityState === "visible") {
- setTimeout(function() {
- if (_this2.connectionIsStale() || !_this2.connection.isOpen()) {
- ActionCable.log("ConnectionMonitor reopening stale connection on visibilitychange. visbilityState = " + document.visibilityState);
- _this2.connection.reopen();
- }
- }, 200);
- }
- };
- return ConnectionMonitor;
- }();
- ConnectionMonitor.pollInterval = {
- min: 3,
- max: 30,
- multiplier: 5
- };
- ConnectionMonitor.staleThreshold = 6;
- var Consumer = function() {
- function Consumer(url) {
- classCallCheck(this, Consumer);
- this.url = url;
- this.subscriptions = new ActionCable.Subscriptions(this);
- this.connection = new ActionCable.Connection(this);
- }
- Consumer.prototype.send = function send(data) {
- return this.connection.send(data);
- };
- Consumer.prototype.connect = function connect() {
- return this.connection.open();
- };
- Consumer.prototype.disconnect = function disconnect() {
- return this.connection.close({
- allowReconnect: false
- });
- };
- Consumer.prototype.ensureActiveConnection = function ensureActiveConnection() {
- if (!this.connection.isActive()) {
- return this.connection.open();
- }
- };
- return Consumer;
- }();
var extend = function extend(object, properties) {
if (properties != null) {
for (var key in properties) {
@@ -350,7 +341,7 @@
var params = (typeof channel === "undefined" ? "undefined" : _typeof(channel)) === "object" ? channel : {
channel: channel
};
- var subscription = new ActionCable.Subscription(this.consumer, params, mixin);
+ var subscription = new Subscription(this.consumer, params, mixin);
return this.add(subscription);
};
Subscriptions.prototype.add = function add(subscription) {
@@ -424,53 +415,65 @@
};
return Subscriptions;
}();
- var ActionCable = {
- Connection: Connection,
- ConnectionMonitor: ConnectionMonitor,
- Consumer: Consumer,
- INTERNAL: INTERNAL,
- Subscription: Subscription,
- Subscriptions: Subscriptions,
- WebSocket: window.WebSocket,
- logger: window.console,
- createConsumer: function createConsumer(url) {
- if (url == null) {
- var urlConfig = this.getConfig("url");
- url = urlConfig ? urlConfig : this.INTERNAL.default_mount_path;
- }
- return new Consumer(this.createWebSocketURL(url));
- },
- getConfig: function getConfig(name) {
- var element = document.head.querySelector("meta[name='action-cable-" + name + "']");
- return element ? element.getAttribute("content") : undefined;
- },
- createWebSocketURL: function createWebSocketURL(url) {
- if (url && !/^wss?:/i.test(url)) {
- var a = document.createElement("a");
- a.href = url;
- a.href = a.href;
- a.protocol = a.protocol.replace("http", "ws");
- return a.href;
- } else {
- return url;
- }
- },
- startDebugging: function startDebugging() {
- this.debugging = true;
- },
- stopDebugging: function stopDebugging() {
- this.debugging = null;
- },
- log: function log() {
- if (this.debugging) {
- var _logger;
- for (var _len = arguments.length, messages = Array(_len), _key = 0; _key < _len; _key++) {
- messages[_key] = arguments[_key];
- }
- messages.push(Date.now());
- (_logger = this.logger).log.apply(_logger, [ "[ActionCable]" ].concat(messages));
+ var Consumer = function() {
+ function Consumer(url) {
+ classCallCheck(this, Consumer);
+ this.url = url;
+ this.subscriptions = new Subscriptions(this);
+ this.connection = new Connection(this);
+ }
+ Consumer.prototype.send = function send(data) {
+ return this.connection.send(data);
+ };
+ Consumer.prototype.connect = function connect() {
+ return this.connection.open();
+ };
+ Consumer.prototype.disconnect = function disconnect() {
+ return this.connection.close({
+ allowReconnect: false
+ });
+ };
+ Consumer.prototype.ensureActiveConnection = function ensureActiveConnection() {
+ if (!this.connection.isActive()) {
+ return this.connection.open();
}
+ };
+ return Consumer;
+ }();
+ function createConsumer(url) {
+ if (url == null) {
+ var urlConfig = getConfig("url");
+ url = urlConfig ? urlConfig : INTERNAL.default_mount_path;
}
- };
- return ActionCable;
+ return new Consumer(createWebSocketURL(url));
+ }
+ function getConfig(name) {
+ var element = document.head.querySelector("meta[name='action-cable-" + name + "']");
+ return element ? element.getAttribute("content") : undefined;
+ }
+ function createWebSocketURL(url) {
+ if (url && !/^wss?:/i.test(url)) {
+ var a = document.createElement("a");
+ a.href = url;
+ a.href = a.href;
+ a.protocol = a.protocol.replace("http", "ws");
+ return a.href;
+ } else {
+ return url;
+ }
+ }
+ exports.Connection = Connection;
+ exports.ConnectionMonitor = ConnectionMonitor;
+ exports.Consumer = Consumer;
+ exports.INTERNAL = INTERNAL;
+ exports.Subscription = Subscription;
+ exports.Subscriptions = Subscriptions;
+ exports.adapters = adapters;
+ exports.logger = logger;
+ exports.createConsumer = createConsumer;
+ exports.getConfig = getConfig;
+ exports.createWebSocketURL = createWebSocketURL;
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
});
diff --git a/actioncable/app/javascript/action_cable/adapters.js b/actioncable/app/javascript/action_cable/adapters.js
new file mode 100644
index 0000000000..9ba6d338ee
--- /dev/null
+++ b/actioncable/app/javascript/action_cable/adapters.js
@@ -0,0 +1,4 @@
+export default {
+ logger: window.console,
+ WebSocket: window.WebSocket
+}
diff --git a/actioncable/app/javascript/action_cable/connection.js b/actioncable/app/javascript/action_cable/connection.js
index 4ad436f2c0..e3ff8bde24 100644
--- a/actioncable/app/javascript/action_cable/connection.js
+++ b/actioncable/app/javascript/action_cable/connection.js
@@ -1,5 +1,7 @@
-import ActionCable from "./index"
+import adapters from "./adapters"
+import ConnectionMonitor from "./connection_monitor"
import INTERNAL from "./internal"
+import logger from "./logger"
// Encapsulate the cable connection held by the consumer. This is an internal class not intended for direct user manipulation.
@@ -13,7 +15,7 @@ class Connection {
this.open = this.open.bind(this)
this.consumer = consumer
this.subscriptions = this.consumer.subscriptions
- this.monitor = new ActionCable.ConnectionMonitor(this)
+ this.monitor = new ConnectionMonitor(this)
this.disconnected = true
}
@@ -28,12 +30,12 @@ class Connection {
open() {
if (this.isActive()) {
- ActionCable.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`)
+ logger.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`)
return false
} else {
- ActionCable.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${protocols}`)
+ logger.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${protocols}`)
if (this.webSocket) { this.uninstallEventHandlers() }
- this.webSocket = new ActionCable.WebSocket(this.consumer.url, protocols)
+ this.webSocket = new adapters.WebSocket(this.consumer.url, protocols)
this.installEventHandlers()
this.monitor.start()
return true
@@ -46,15 +48,15 @@ class Connection {
}
reopen() {
- ActionCable.log(`Reopening WebSocket, current state is ${this.getState()}`)
+ logger.log(`Reopening WebSocket, current state is ${this.getState()}`)
if (this.isActive()) {
try {
return this.close()
} catch (error) {
- ActionCable.log("Failed to reopen WebSocket", error)
+ logger.log("Failed to reopen WebSocket", error)
}
finally {
- ActionCable.log(`Reopening WebSocket in ${this.constructor.reopenDelay}ms`)
+ logger.log(`Reopening WebSocket in ${this.constructor.reopenDelay}ms`)
setTimeout(this.open, this.constructor.reopenDelay)
}
} else {
@@ -132,16 +134,16 @@ Connection.prototype.events = {
},
open() {
- ActionCable.log(`WebSocket onopen event, using '${this.getProtocol()}' subprotocol`)
+ logger.log(`WebSocket onopen event, using '${this.getProtocol()}' subprotocol`)
this.disconnected = false
if (!this.isProtocolSupported()) {
- ActionCable.log("Protocol is unsupported. Stopping monitor and disconnecting.")
+ logger.log("Protocol is unsupported. Stopping monitor and disconnecting.")
return this.close({allowReconnect: false})
}
},
close(event) {
- ActionCable.log("WebSocket onclose event")
+ logger.log("WebSocket onclose event")
if (this.disconnected) { return }
this.disconnected = true
this.monitor.recordDisconnect()
@@ -149,7 +151,7 @@ Connection.prototype.events = {
},
error() {
- ActionCable.log("WebSocket onerror event")
+ logger.log("WebSocket onerror event")
}
}
diff --git a/actioncable/app/javascript/action_cable/connection_monitor.js b/actioncable/app/javascript/action_cable/connection_monitor.js
index cd1e4602d8..f0e75ae137 100644
--- a/actioncable/app/javascript/action_cable/connection_monitor.js
+++ b/actioncable/app/javascript/action_cable/connection_monitor.js
@@ -1,4 +1,4 @@
-import ActionCable from "./index"
+import logger from "./logger"
// Responsible for ensuring the cable connection is in good health by validating the heartbeat pings sent from the server, and attempting
// revival reconnections if things go astray. Internal class, not intended for direct user manipulation.
@@ -22,7 +22,7 @@ class ConnectionMonitor {
delete this.stoppedAt
this.startPolling()
document.addEventListener("visibilitychange", this.visibilityDidChange)
- ActionCable.log(`ConnectionMonitor started. pollInterval = ${this.getPollInterval()} ms`)
+ logger.log(`ConnectionMonitor started. pollInterval = ${this.getPollInterval()} ms`)
}
}
@@ -31,7 +31,7 @@ class ConnectionMonitor {
this.stoppedAt = now()
this.stopPolling()
document.removeEventListener("visibilitychange", this.visibilityDidChange)
- ActionCable.log("ConnectionMonitor stopped")
+ logger.log("ConnectionMonitor stopped")
}
}
@@ -47,12 +47,12 @@ class ConnectionMonitor {
this.reconnectAttempts = 0
this.recordPing()
delete this.disconnectedAt
- ActionCable.log("ConnectionMonitor recorded connect")
+ logger.log("ConnectionMonitor recorded connect")
}
recordDisconnect() {
this.disconnectedAt = now()
- ActionCable.log("ConnectionMonitor recorded disconnect")
+ logger.log("ConnectionMonitor recorded disconnect")
}
// Private
@@ -82,12 +82,12 @@ class ConnectionMonitor {
reconnectIfStale() {
if (this.connectionIsStale()) {
- 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`)
+ 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`)
this.reconnectAttempts++
if (this.disconnectedRecently()) {
- ActionCable.log("ConnectionMonitor skipping reopening recent disconnect")
+ logger.log("ConnectionMonitor skipping reopening recent disconnect")
} else {
- ActionCable.log("ConnectionMonitor reopening")
+ logger.log("ConnectionMonitor reopening")
this.connection.reopen()
}
}
@@ -105,7 +105,7 @@ class ConnectionMonitor {
if (document.visibilityState === "visible") {
setTimeout(() => {
if (this.connectionIsStale() || !this.connection.isOpen()) {
- ActionCable.log(`ConnectionMonitor reopening stale connection on visibilitychange. visbilityState = ${document.visibilityState}`)
+ logger.log(`ConnectionMonitor reopening stale connection on visibilitychange. visbilityState = ${document.visibilityState}`)
this.connection.reopen()
}
}
diff --git a/actioncable/app/javascript/action_cable/consumer.js b/actioncable/app/javascript/action_cable/consumer.js
index c484ceebbd..e8440f39f5 100644
--- a/actioncable/app/javascript/action_cable/consumer.js
+++ b/actioncable/app/javascript/action_cable/consumer.js
@@ -1,4 +1,5 @@
-import ActionCable from "./index"
+import Connection from "./connection"
+import Subscriptions from "./subscriptions"
// The ActionCable.Consumer establishes the connection to a server-side Ruby Connection object. Once established,
// the ActionCable.ConnectionMonitor will ensure that its properly maintained through heartbeats and checking for stale updates.
@@ -29,8 +30,8 @@ import ActionCable from "./index"
export default class Consumer {
constructor(url) {
this.url = url
- this.subscriptions = new ActionCable.Subscriptions(this)
- this.connection = new ActionCable.Connection(this)
+ this.subscriptions = new Subscriptions(this)
+ this.connection = new Connection(this)
}
send(data) {
diff --git a/actioncable/app/javascript/action_cable/index.js b/actioncable/app/javascript/action_cable/index.js
index eb0c4844df..9f41c14e94 100644
--- a/actioncable/app/javascript/action_cable/index.js
+++ b/actioncable/app/javascript/action_cable/index.js
@@ -4,55 +4,42 @@ import Consumer from "./consumer"
import INTERNAL from "./internal"
import Subscription from "./subscription"
import Subscriptions from "./subscriptions"
+import adapters from "./adapters"
+import logger from "./logger"
-export default {
+export {
Connection,
ConnectionMonitor,
Consumer,
INTERNAL,
Subscription,
Subscriptions,
- WebSocket: window.WebSocket,
- logger: window.console,
-
- createConsumer(url) {
- if (url == null) {
- const urlConfig = this.getConfig("url")
- url = (urlConfig ? urlConfig : this.INTERNAL.default_mount_path)
- }
- return new Consumer(this.createWebSocketURL(url))
- },
-
- getConfig(name) {
- const element = document.head.querySelector(`meta[name='action-cable-${name}']`)
- return (element ? element.getAttribute("content") : undefined)
- },
-
- createWebSocketURL(url) {
- if (url && !/^wss?:/i.test(url)) {
- const a = document.createElement("a")
- a.href = url
- // Fix populating Location properties in IE. Otherwise, protocol will be blank.
- a.href = a.href
- a.protocol = a.protocol.replace("http", "ws")
- return a.href
- } else {
- return url
- }
- },
+ adapters,
+ logger,
+}
- startDebugging() {
- this.debugging = true
- },
+export function createConsumer(url) {
+ if (url == null) {
+ const urlConfig = getConfig("url")
+ url = (urlConfig ? urlConfig : INTERNAL.default_mount_path)
+ }
+ return new Consumer(createWebSocketURL(url))
+}
- stopDebugging() {
- this.debugging = null
- },
+export function getConfig(name) {
+ const element = document.head.querySelector(`meta[name='action-cable-${name}']`)
+ return (element ? element.getAttribute("content") : undefined)
+}
- log(...messages) {
- if (this.debugging) {
- messages.push(Date.now())
- this.logger.log("[ActionCable]", ...messages)
- }
+export function createWebSocketURL(url) {
+ if (url && !/^wss?:/i.test(url)) {
+ const a = document.createElement("a")
+ a.href = url
+ // Fix populating Location properties in IE. Otherwise, protocol will be blank.
+ a.href = a.href
+ a.protocol = a.protocol.replace("http", "ws")
+ return a.href
+ } else {
+ return url
}
}
diff --git a/actioncable/app/javascript/action_cable/logger.js b/actioncable/app/javascript/action_cable/logger.js
new file mode 100644
index 0000000000..ef4661ead1
--- /dev/null
+++ b/actioncable/app/javascript/action_cable/logger.js
@@ -0,0 +1,10 @@
+import adapters from "./adapters"
+
+export default {
+ log(...messages) {
+ if (this.enabled) {
+ messages.push(Date.now())
+ adapters.logger.log("[ActionCable]", ...messages)
+ }
+ },
+}
diff --git a/actioncable/app/javascript/action_cable/subscriptions.js b/actioncable/app/javascript/action_cable/subscriptions.js
index 712ff50d28..867cafb407 100644
--- a/actioncable/app/javascript/action_cable/subscriptions.js
+++ b/actioncable/app/javascript/action_cable/subscriptions.js
@@ -1,4 +1,4 @@
-import ActionCable from "./index"
+import Subscription from "./subscription"
// Collection class for creating (and internally managing) channel subscriptions. The only method intended to be triggered by the user
// us ActionCable.Subscriptions#create, and it should be called through the consumer like so:
@@ -18,7 +18,7 @@ export default class Subscriptions {
create(channelName, mixin) {
const channel = channelName
const params = typeof channel === "object" ? channel : {channel}
- const subscription = new ActionCable.Subscription(this.consumer, params, mixin)
+ const subscription = new Subscription(this.consumer, params, mixin)
return this.add(subscription)
}
diff --git a/actioncable/package.json b/actioncable/package.json
index ba641b64a3..db78c1a09a 100644
--- a/actioncable/package.json
+++ b/actioncable/package.json
@@ -4,7 +4,8 @@
"description": "WebSocket framework for Ruby on Rails.",
"main": "app/assets/javascripts/action_cable.js",
"files": [
- "app/assets/javascripts/*.js"
+ "app/assets/javascripts/*.js",
+ "src/*.js"
],
"repository": {
"type": "git",
@@ -43,6 +44,7 @@
"prebuild": "yarn lint && bundle exec rake assets:codegen",
"build": "rollup --config rollup.config.js",
"lint": "eslint app/javascript",
+ "prepublishOnly": "rm -rf src && cp -R app/javascript/action_cable src",
"pretest": "bundle exec rake assets:codegen && rollup --config rollup.config.test.js",
"test": "karma start"
}
diff --git a/actioncable/rollup.config.js b/actioncable/rollup.config.js
index 03046526aa..64727e0887 100644
--- a/actioncable/rollup.config.js
+++ b/actioncable/rollup.config.js
@@ -1,5 +1,3 @@
-import resolve from "rollup-plugin-node-resolve"
-import commonjs from "rollup-plugin-commonjs"
import babel from "rollup-plugin-babel"
import uglify from "rollup-plugin-uglify"
@@ -20,8 +18,6 @@ export default {
name: "ActionCable"
},
plugins: [
- resolve(),
- commonjs(),
babel(),
uglify(uglifyOptions)
]
diff --git a/actioncable/test/javascript/src/test_helpers/consumer_test_helper.js b/actioncable/test/javascript/src/test_helpers/consumer_test_helper.js
index f1abea331b..d1dabc9fc4 100644
--- a/actioncable/test/javascript/src/test_helpers/consumer_test_helper.js
+++ b/actioncable/test/javascript/src/test_helpers/consumer_test_helper.js
@@ -1,5 +1,5 @@
import { WebSocket as MockWebSocket, Server as MockServer } from "mock-socket"
-import ActionCable from "../../../../app/javascript/action_cable/index"
+import * as ActionCable from "../../../../app/javascript/action_cable/index"
import {defer, testURL} from "./index"
export default function(name, options, callback) {
@@ -14,7 +14,7 @@ export default function(name, options, callback) {
return QUnit.test(name, function(assert) {
const doneAsync = assert.async()
- ActionCable.WebSocket = MockWebSocket
+ ActionCable.adapters.WebSocket = MockWebSocket
const server = new MockServer(options.url)
const consumer = ActionCable.createConsumer(options.url)
diff --git a/actioncable/test/javascript/src/test_helpers/index.js b/actioncable/test/javascript/src/test_helpers/index.js
index 5fa46c21ae..0cd4e260b3 100644
--- a/actioncable/test/javascript/src/test_helpers/index.js
+++ b/actioncable/test/javascript/src/test_helpers/index.js
@@ -1,4 +1,4 @@
-import ActionCable from "../../../../app/javascript/action_cable/index"
+import * as ActionCable from "../../../../app/javascript/action_cable/index"
export const testURL = "ws://cable.example.com/"
@@ -6,5 +6,5 @@ export function defer(callback) {
setTimeout(callback, 1)
}
-const originalWebSocket = ActionCable.WebSocket
-QUnit.testDone(() => ActionCable.WebSocket = originalWebSocket)
+const originalWebSocket = ActionCable.adapters.WebSocket
+QUnit.testDone(() => ActionCable.adapters.WebSocket = originalWebSocket)
diff --git a/actioncable/test/javascript/src/unit/action_cable_test.js b/actioncable/test/javascript/src/unit/action_cable_test.js
index 8847d87545..daad900aca 100644
--- a/actioncable/test/javascript/src/unit/action_cable_test.js
+++ b/actioncable/test/javascript/src/unit/action_cable_test.js
@@ -1,4 +1,4 @@
-import ActionCable from "../../../../app/javascript/action_cable/index"
+import * as ActionCable from "../../../../app/javascript/action_cable/index"
import {testURL} from "../test_helpers/index"
const {module, test} = QUnit
@@ -7,23 +7,13 @@ module("ActionCable", () => {
module("Adapters", () => {
module("WebSocket", () => {
test("default is window.WebSocket", assert => {
- assert.equal(ActionCable.WebSocket, window.WebSocket)
- })
-
- test("configurable", assert => {
- ActionCable.WebSocket = ""
- assert.equal(ActionCable.WebSocket, "")
+ assert.equal(ActionCable.adapters.WebSocket, window.WebSocket)
})
})
module("logger", () => {
test("default is window.console", assert => {
- assert.equal(ActionCable.logger, window.console)
- })
-
- test("configurable", assert => {
- ActionCable.logger = ""
- assert.equal(ActionCable.logger, "")
+ assert.equal(ActionCable.adapters.logger, window.console)
})
})
})