From 6d488a22d361d19e3de98c603d98e64ccea8a2f3 Mon Sep 17 00:00:00 2001 From: Ryan Castner Date: Sun, 31 Mar 2019 13:41:12 -0400 Subject: feat(js): Dynamic ActionCable URL (#35579) * Failing test case * feat: Dynamic Url Generation Change createWebSocketURL to be a closure that allows url to be evaluated at the time the webSocket is established * refactor: createWebSocketURL to Consumer, remove need for closure Move initial call to createWebSocketURL in createConsumer * docs: Add documentation for dynamic url and string args to createConsumer Co-Authored-By: rmacklin [Ryan Castner, rmacklin] --- actioncable/app/assets/javascripts/action_cable.js | 46 ++++++++++++++++------ .../app/javascript/action_cable/consumer.js | 21 +++++++++- actioncable/app/javascript/action_cable/index.js | 20 ++-------- .../test/javascript/src/unit/action_cable_test.js | 7 +++- 4 files changed, 62 insertions(+), 32 deletions(-) (limited to 'actioncable') diff --git a/actioncable/app/assets/javascripts/action_cable.js b/actioncable/app/assets/javascripts/action_cable.js index 4efab2ed46..029c7567ce 100644 --- a/actioncable/app/assets/javascripts/action_cable.js +++ b/actioncable/app/assets/javascripts/action_cable.js @@ -28,6 +28,22 @@ throw new TypeError("Cannot call a class as a function"); } }; + var createClass = function() { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + return function(Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; + }(); var now = function now() { return new Date().getTime(); }; @@ -432,7 +448,7 @@ var Consumer = function() { function Consumer(url) { classCallCheck(this, Consumer); - this.url = url; + this._url = url; this.subscriptions = new Subscriptions(this); this.connection = new Connection(this); } @@ -452,18 +468,14 @@ return this.connection.open(); } }; + createClass(Consumer, [ { + key: "url", + get: function get$$1() { + return createWebSocketURL(this._url); + } + } ]); return Consumer; }(); - function createConsumer() { - var url = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getConfig("url") || INTERNAL.default_mount_path; - return new Consumer(createWebSocketURL(url)); - } - function getConfig(name) { - var element = document.head.querySelector("meta[name='action-cable-" + name + "']"); - if (element) { - return element.getAttribute("content"); - } - } function createWebSocketURL(url) { var webSocketURL = typeof url === "function" ? url() : url; if (webSocketURL && !/^wss?:/i.test(webSocketURL)) { @@ -476,6 +488,16 @@ return webSocketURL; } } + function createConsumer() { + var url = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getConfig("url") || INTERNAL.default_mount_path; + return new Consumer(url); + } + function getConfig(name) { + var element = document.head.querySelector("meta[name='action-cable-" + name + "']"); + if (element) { + return element.getAttribute("content"); + } + } exports.Connection = Connection; exports.ConnectionMonitor = ConnectionMonitor; exports.Consumer = Consumer; @@ -484,9 +506,9 @@ exports.Subscriptions = Subscriptions; exports.adapters = adapters; exports.logger = logger; + exports.createWebSocketURL = createWebSocketURL; exports.createConsumer = createConsumer; exports.getConfig = getConfig; - exports.createWebSocketURL = createWebSocketURL; Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/actioncable/app/javascript/action_cable/consumer.js b/actioncable/app/javascript/action_cable/consumer.js index e8440f39f5..51f3b60980 100644 --- a/actioncable/app/javascript/action_cable/consumer.js +++ b/actioncable/app/javascript/action_cable/consumer.js @@ -29,11 +29,15 @@ import Subscriptions from "./subscriptions" export default class Consumer { constructor(url) { - this.url = url + this._url = url this.subscriptions = new Subscriptions(this) this.connection = new Connection(this) } + get url() { + return createWebSocketURL(this._url) + } + send(data) { return this.connection.send(data) } @@ -52,3 +56,18 @@ export default class Consumer { } } } + +export function createWebSocketURL(url) { + const webSocketURL = typeof url === "function" ? url() : url + + if (webSocketURL && !/^wss?:/i.test(webSocketURL)) { + const a = document.createElement("a") + a.href = webSocketURL + // 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 webSocketURL + } +} diff --git a/actioncable/app/javascript/action_cable/index.js b/actioncable/app/javascript/action_cable/index.js index e679745fd7..d484d99179 100644 --- a/actioncable/app/javascript/action_cable/index.js +++ b/actioncable/app/javascript/action_cable/index.js @@ -1,6 +1,6 @@ import Connection from "./connection" import ConnectionMonitor from "./connection_monitor" -import Consumer from "./consumer" +import Consumer, { createWebSocketURL } from "./consumer" import INTERNAL from "./internal" import Subscription from "./subscription" import Subscriptions from "./subscriptions" @@ -16,10 +16,11 @@ export { Subscriptions, adapters, logger, + createWebSocketURL, } export function createConsumer(url = getConfig("url") || INTERNAL.default_mount_path) { - return new Consumer(createWebSocketURL(url)) + return new Consumer(url) } export function getConfig(name) { @@ -28,18 +29,3 @@ export function getConfig(name) { return element.getAttribute("content") } } - -export function createWebSocketURL(url) { - const webSocketURL = typeof url === "function" ? url() : url - - if (webSocketURL && !/^wss?:/i.test(webSocketURL)) { - const a = document.createElement("a") - a.href = webSocketURL - // 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 webSocketURL - } -} diff --git a/actioncable/test/javascript/src/unit/action_cable_test.js b/actioncable/test/javascript/src/unit/action_cable_test.js index c9d34abc6d..2181f955e3 100644 --- a/actioncable/test/javascript/src/unit/action_cable_test.js +++ b/actioncable/test/javascript/src/unit/action_cable_test.js @@ -43,11 +43,14 @@ module("ActionCable", () => { }) test("uses function to generate URL", assert => { + let dynamicURL = testURL const generateURL = () => { - return testURL + return dynamicURL } + + dynamicURL = `${testURL}foo` const consumer = ActionCable.createConsumer(generateURL) - assert.equal(consumer.url, testURL) + assert.equal(consumer.url, `${testURL}foo`) }) }) }) -- cgit v1.2.3