aboutsummaryrefslogtreecommitdiffstats
path: root/actioncable/app
diff options
context:
space:
mode:
Diffstat (limited to 'actioncable/app')
-rw-r--r--actioncable/app/javascript/action_cable/connection.js268
-rw-r--r--actioncable/app/javascript/action_cable/connection_monitor.js79
-rw-r--r--actioncable/app/javascript/action_cable/consumer.js11
-rw-r--r--actioncable/app/javascript/action_cable/index.js.erb23
-rw-r--r--actioncable/app/javascript/action_cable/subscription.js80
-rw-r--r--actioncable/app/javascript/action_cable/subscriptions.js46
6 files changed, 228 insertions, 279 deletions
diff --git a/actioncable/app/javascript/action_cable/connection.js b/actioncable/app/javascript/action_cable/connection.js
index 462db965d1..540f597303 100644
--- a/actioncable/app/javascript/action_cable/connection.js
+++ b/actioncable/app/javascript/action_cable/connection.js
@@ -1,164 +1,158 @@
-/*
- * decaffeinate suggestions:
- * DS101: Remove unnecessary use of Array.from
- * DS102: Remove unnecessary code created because of implicit returns
- * DS104: Avoid inline assignments
- * DS201: Simplify complex destructure assignments
- * DS204: Change includes calls to have a more natural evaluation order
- * DS206: Consider reworking classes to avoid initClass
- * DS207: Consider shorter variations of null checks
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
- */
//= require ./connection_monitor
// Encapsulate the cable connection held by the consumer. This is an internal class not intended for direct user manipulation.
const {message_types, protocols} = ActionCable.INTERNAL
-const adjustedLength = Math.max(protocols.length, 1),
- supportedProtocols = protocols.slice(0, adjustedLength - 1),
- unsupportedProtocol = protocols[adjustedLength - 1]
-
-const Cls = (ActionCable.Connection = class Connection {
- static initClass() {
- this.reopenDelay = 500
-
- this.prototype.events = {
- message(event) {
- if (!this.isProtocolSupported()) { return }
- const {identifier, message, type} = JSON.parse(event.data)
- switch (type) {
- case message_types.welcome:
- this.monitor.recordConnect()
- return this.subscriptions.reload()
- case message_types.ping:
- return this.monitor.recordPing()
- case message_types.confirmation:
- return this.subscriptions.notify(identifier, "connected")
- case message_types.rejection:
- return this.subscriptions.reject(identifier)
- default:
- return this.subscriptions.notify(identifier, "received", message)
- }
- },
-
- open() {
- ActionCable.log(`WebSocket onopen event, using '${this.getProtocol()}' subprotocol`)
- this.disconnected = false
- if (!this.isProtocolSupported()) {
- ActionCable.log("Protocol is unsupported. Stopping monitor and disconnecting.")
- return this.close({allowReconnect: false})
- }
- },
-
- close(event) {
- ActionCable.log("WebSocket onclose event")
- if (this.disconnected) { return }
- this.disconnected = true
- this.monitor.recordDisconnect()
- return this.subscriptions.notifyAll("disconnected", {willAttemptReconnect: this.monitor.isRunning()})
- },
-
- error() {
- return ActionCable.log("WebSocket onerror event")
- }
+const supportedProtocols = protocols.slice(0, protocols.length - 1)
+
+ActionCable.Connection = (function() {
+ const indexOf = [].indexOf
+
+ class Connection {
+ constructor(consumer) {
+ this.open = this.open.bind(this)
+ this.consumer = consumer
+ this.subscriptions = this.consumer.subscriptions
+ this.monitor = new ActionCable.ConnectionMonitor(this)
+ this.disconnected = true
}
- }
-
- constructor(consumer) {
- this.open = this.open.bind(this)
- this.consumer = consumer;
- ({subscriptions: this.subscriptions} = this.consumer)
- this.monitor = new ActionCable.ConnectionMonitor(this)
- this.disconnected = true
- }
- send(data) {
- if (this.isOpen()) {
- this.webSocket.send(JSON.stringify(data))
- return true
- } else {
- return false
+ send(data) {
+ if (this.isOpen()) {
+ this.webSocket.send(JSON.stringify(data))
+ return true
+ } else {
+ return false
+ }
}
- }
- open() {
- if (this.isActive()) {
- ActionCable.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}`)
- if (this.webSocket != null) { this.uninstallEventHandlers() }
- this.webSocket = new ActionCable.WebSocket(this.consumer.url, protocols)
- this.installEventHandlers()
- this.monitor.start()
- return true
+ open() {
+ if (this.isActive()) {
+ ActionCable.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}`)
+ if (this.webSocket) { this.uninstallEventHandlers() }
+ this.webSocket = new ActionCable.WebSocket(this.consumer.url, protocols)
+ this.installEventHandlers()
+ this.monitor.start()
+ return true
+ }
}
- }
- close(param) {
- if (param == null) { param = {allowReconnect: true} }
- const {allowReconnect} = param
- if (!allowReconnect) { this.monitor.stop() }
- if (this.isActive()) { return (this.webSocket != null ? this.webSocket.close() : undefined) }
- }
+ close({allowReconnect} = {allowReconnect: true}) {
+ if (!allowReconnect) { this.monitor.stop() }
+ if (this.isActive()) { return (this.webSocket ? this.webSocket.close() : undefined) }
+ }
- reopen() {
- ActionCable.log(`Reopening WebSocket, current state is ${this.getState()}`)
- if (this.isActive()) {
- try {
- return this.close()
- } catch (error) {
- return ActionCable.log("Failed to reopen WebSocket", error)
- }
- finally {
- ActionCable.log(`Reopening WebSocket in ${this.constructor.reopenDelay}ms`)
- setTimeout(this.open, this.constructor.reopenDelay)
+ reopen() {
+ ActionCable.log(`Reopening WebSocket, current state is ${this.getState()}`)
+ if (this.isActive()) {
+ try {
+ return this.close()
+ } catch (error) {
+ ActionCable.log("Failed to reopen WebSocket", error)
+ }
+ finally {
+ ActionCable.log(`Reopening WebSocket in ${this.constructor.reopenDelay}ms`)
+ setTimeout(this.open, this.constructor.reopenDelay)
+ }
+ } else {
+ return this.open()
}
- } else {
- return this.open()
}
- }
- getProtocol() {
- return (this.webSocket != null ? this.webSocket.protocol : undefined)
- }
+ getProtocol() {
+ return (this.webSocket ? this.webSocket.protocol : undefined)
+ }
- isOpen() {
- return this.isState("open")
- }
+ isOpen() {
+ return this.isState("open")
+ }
- isActive() {
- return this.isState("open", "connecting")
- }
+ isActive() {
+ return this.isState("open", "connecting")
+ }
- // Private
+ // Private
- isProtocolSupported() {
- let needle
- return (needle = this.getProtocol(), Array.from(supportedProtocols).includes(needle))
- }
+ isProtocolSupported() {
+ return indexOf.call(supportedProtocols, this.getProtocol()) >= 0
+ }
- isState(...states) {
- let needle
- return (needle = this.getState(), Array.from(states).includes(needle))
- }
+ isState(...states) {
+ return indexOf.call(states, this.getState()) >= 0
+ }
- getState() {
- for (let state in WebSocket) { const value = WebSocket[state]; if (value === (this.webSocket != null ? this.webSocket.readyState : undefined)) { return state.toLowerCase() } }
- return null
- }
+ getState() {
+ if (this.webSocket) {
+ for (let state in WebSocket) {
+ if (WebSocket[state] === this.webSocket.readyState) {
+ return state.toLowerCase()
+ }
+ }
+ }
+ return null
+ }
- installEventHandlers() {
- for (let eventName in this.events) {
- const handler = this.events[eventName].bind(this)
- this.webSocket[`on${eventName}`] = handler
+ installEventHandlers() {
+ for (let eventName in this.events) {
+ const handler = this.events[eventName].bind(this)
+ this.webSocket[`on${eventName}`] = handler
+ }
}
+
+ uninstallEventHandlers() {
+ for (let eventName in this.events) {
+ this.webSocket[`on${eventName}`] = function() {}
+ }
+ }
+
}
- uninstallEventHandlers() {
- for (let eventName in this.events) {
- this.webSocket[`on${eventName}`] = function() {}
+ Connection.reopenDelay = 500
+
+ Connection.prototype.events = {
+ message(event) {
+ if (!this.isProtocolSupported()) { return }
+ const {identifier, message, type} = JSON.parse(event.data)
+ switch (type) {
+ case message_types.welcome:
+ this.monitor.recordConnect()
+ return this.subscriptions.reload()
+ case message_types.ping:
+ return this.monitor.recordPing()
+ case message_types.confirmation:
+ return this.subscriptions.notify(identifier, "connected")
+ case message_types.rejection:
+ return this.subscriptions.reject(identifier)
+ default:
+ return this.subscriptions.notify(identifier, "received", message)
+ }
+ },
+
+ open() {
+ ActionCable.log(`WebSocket onopen event, using '${this.getProtocol()}' subprotocol`)
+ this.disconnected = false
+ if (!this.isProtocolSupported()) {
+ ActionCable.log("Protocol is unsupported. Stopping monitor and disconnecting.")
+ return this.close({allowReconnect: false})
+ }
+ },
+
+ close(event) {
+ ActionCable.log("WebSocket onclose event")
+ if (this.disconnected) { return }
+ this.disconnected = true
+ this.monitor.recordDisconnect()
+ return this.subscriptions.notifyAll("disconnected", {willAttemptReconnect: this.monitor.isRunning()})
+ },
+
+ error() {
+ ActionCable.log("WebSocket onerror event")
}
}
-})
-Cls.initClass()
+
+ return Connection
+
+})()
diff --git a/actioncable/app/javascript/action_cable/connection_monitor.js b/actioncable/app/javascript/action_cable/connection_monitor.js
index c8d2c62cc8..4d2db5b4ae 100644
--- a/actioncable/app/javascript/action_cable/connection_monitor.js
+++ b/actioncable/app/javascript/action_cable/connection_monitor.js
@@ -1,33 +1,13 @@
-/*
- * decaffeinate suggestions:
- * DS102: Remove unnecessary code created because of implicit returns
- * DS206: Consider reworking classes to avoid initClass
- * DS207: Consider shorter variations of null checks
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
- */
// 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.
-(function() {
- let now = undefined
- let secondsSince = undefined
- let clamp = undefined
- const Cls = (ActionCable.ConnectionMonitor = class ConnectionMonitor {
- static initClass() {
- this.pollInterval = {
- min: 3,
- max: 30
- }
-
- this.staleThreshold = 6
-
- now = () => new Date().getTime()
-
- secondsSince = time => (now() - time) / 1000
-
- clamp = (number, min, max) => Math.max(min, Math.min(max, number))
- // Server::Connections::BEAT_INTERVAL * 2 (missed two pings)
- }
+ActionCable.ConnectionMonitor = (function() {
+ const now = () => new Date().getTime()
+
+ const secondsSince = time => (now() - time) / 1000
+ const clamp = (number, min, max) => Math.max(min, Math.min(max, number))
+
+ class ConnectionMonitor {
constructor(connection) {
this.visibilityDidChange = this.visibilityDidChange.bind(this)
this.connection = connection
@@ -40,7 +20,7 @@
delete this.stoppedAt
this.startPolling()
document.addEventListener("visibilitychange", this.visibilityDidChange)
- return ActionCable.log(`ConnectionMonitor started. pollInterval = ${this.getPollInterval()} ms`)
+ ActionCable.log(`ConnectionMonitor started. pollInterval = ${this.getPollInterval()} ms`)
}
}
@@ -49,45 +29,45 @@
this.stoppedAt = now()
this.stopPolling()
document.removeEventListener("visibilitychange", this.visibilityDidChange)
- return ActionCable.log("ConnectionMonitor stopped")
+ ActionCable.log("ConnectionMonitor stopped")
}
}
isRunning() {
- return (this.startedAt != null) && (this.stoppedAt == null)
+ return this.startedAt && !this.stoppedAt
}
recordPing() {
- return this.pingedAt = now()
+ this.pingedAt = now()
}
recordConnect() {
this.reconnectAttempts = 0
this.recordPing()
delete this.disconnectedAt
- return ActionCable.log("ConnectionMonitor recorded connect")
+ ActionCable.log("ConnectionMonitor recorded connect")
}
recordDisconnect() {
this.disconnectedAt = now()
- return ActionCable.log("ConnectionMonitor recorded disconnect")
+ ActionCable.log("ConnectionMonitor recorded disconnect")
}
// Private
startPolling() {
this.stopPolling()
- return this.poll()
+ this.poll()
}
stopPolling() {
- return clearTimeout(this.pollTimeout)
+ clearTimeout(this.pollTimeout)
}
poll() {
- return this.pollTimeout = setTimeout(() => {
+ this.pollTimeout = setTimeout(() => {
this.reconnectIfStale()
- return this.poll()
+ this.poll()
}
, this.getPollInterval())
}
@@ -103,16 +83,16 @@
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()) {
- return ActionCable.log("ConnectionMonitor skipping reopening recent disconnect")
+ ActionCable.log("ConnectionMonitor skipping reopening recent disconnect")
} else {
ActionCable.log("ConnectionMonitor reopening")
- return this.connection.reopen()
+ this.connection.reopen()
}
}
}
connectionIsStale() {
- return secondsSince(this.pingedAt != null ? this.pingedAt : this.startedAt) > this.constructor.staleThreshold
+ return secondsSince(this.pingedAt ? this.pingedAt : this.startedAt) > this.constructor.staleThreshold
}
disconnectedRecently() {
@@ -121,16 +101,25 @@
visibilityDidChange() {
if (document.visibilityState === "visible") {
- return setTimeout(() => {
+ setTimeout(() => {
if (this.connectionIsStale() || !this.connection.isOpen()) {
ActionCable.log(`ConnectionMonitor reopening stale connection on visibilitychange. visbilityState = ${document.visibilityState}`)
- return this.connection.reopen()
+ this.connection.reopen()
}
}
, 200)
}
}
- })
- Cls.initClass()
- return Cls
+
+ }
+
+ ConnectionMonitor.pollInterval = {
+ min: 3,
+ max: 30
+ }
+
+ ConnectionMonitor.staleThreshold = 6 // Server::Connections::BEAT_INTERVAL * 2 (missed two pings)
+
+ return ConnectionMonitor
+
})()
diff --git a/actioncable/app/javascript/action_cable/consumer.js b/actioncable/app/javascript/action_cable/consumer.js
index c2a851b876..731e00996d 100644
--- a/actioncable/app/javascript/action_cable/consumer.js
+++ b/actioncable/app/javascript/action_cable/consumer.js
@@ -1,8 +1,3 @@
-/*
- * decaffeinate suggestions:
- * DS102: Remove unnecessary code created because of implicit returns
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
- */
//= require ./connection
//= require ./subscriptions
//= require ./subscription
@@ -14,9 +9,9 @@
//
// The following example shows how this can be setup:
//
-// @App = {}
-// App.cable = ActionCable.createConsumer "ws://example.com/accounts/1"
-// App.appearance = App.cable.subscriptions.create "AppearanceChannel"
+// App = {}
+// App.cable = ActionCable.createConsumer("ws://example.com/accounts/1")
+// App.appearance = App.cable.subscriptions.create("AppearanceChannel")
//
// For more details on how you'd configure an actual channel subscription, see ActionCable.Subscription.
//
diff --git a/actioncable/app/javascript/action_cable/index.js.erb b/actioncable/app/javascript/action_cable/index.js.erb
index a5fb5b4556..eb85eba722 100644
--- a/actioncable/app/javascript/action_cable/index.js.erb
+++ b/actioncable/app/javascript/action_cable/index.js.erb
@@ -1,12 +1,3 @@
-/*
- * decaffeinate suggestions:
- * DS101: Remove unnecessary use of Array.from
- * DS102: Remove unnecessary code created because of implicit returns
- * DS104: Avoid inline assignments
- * DS207: Consider shorter variations of null checks
- * DS208: Avoid top-level this
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
- */
//= export ActionCable
//= require_self
//= require ./action_cable/consumer
@@ -17,14 +8,16 @@ this.ActionCable = {
logger: window.console,
createConsumer(url) {
- if (url == null) { let left
- url = (left = this.getConfig("url")) != null ? left : this.INTERNAL.default_mount_path }
+ if (url == null) {
+ const urlConfig = this.getConfig("url")
+ url = (urlConfig ? urlConfig : this.INTERNAL.default_mount_path)
+ }
return new ActionCable.Consumer(this.createWebSocketURL(url))
},
getConfig(name) {
const element = document.head.querySelector(`meta[name='action-cable-${name}']`)
- return (element != null ? element.getAttribute("content") : undefined)
+ return (element ? element.getAttribute("content") : undefined)
},
createWebSocketURL(url) {
@@ -41,17 +34,17 @@ this.ActionCable = {
},
startDebugging() {
- return this.debugging = true
+ this.debugging = true
},
stopDebugging() {
- return this.debugging = null
+ this.debugging = null
},
log(...messages) {
if (this.debugging) {
messages.push(Date.now())
- return this.logger.log("[ActionCable]", ...Array.from(messages))
+ this.logger.log("[ActionCable]", ...messages)
}
}
}
diff --git a/actioncable/app/javascript/action_cable/subscription.js b/actioncable/app/javascript/action_cable/subscription.js
index 3659c9ca46..95b9ff6042 100644
--- a/actioncable/app/javascript/action_cable/subscription.js
+++ b/actioncable/app/javascript/action_cable/subscription.js
@@ -1,37 +1,36 @@
-/*
- * decaffeinate suggestions:
- * DS102: Remove unnecessary code created because of implicit returns
- * DS206: Consider reworking classes to avoid initClass
- * DS207: Consider shorter variations of null checks
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
- */
// A new subscription is created through the ActionCable.Subscriptions instance available on the consumer.
// It provides a number of callbacks and a method for calling remote procedure calls on the corresponding
// Channel instance on the server side.
//
// An example demonstrates the basic functionality:
//
-// App.appearance = App.cable.subscriptions.create "AppearanceChannel",
-// connected: ->
-// # Called once the subscription has been successfully completed
+// App.appearance = App.cable.subscriptions.create("AppearanceChannel", {
+// connected() {
+// // Called once the subscription has been successfully completed
+// },
//
-// disconnected: ({ willAttemptReconnect: boolean }) ->
-// # Called when the client has disconnected with the server.
-// # The object will have an `willAttemptReconnect` property which
-// # says whether the client has the intention of attempting
-// # to reconnect.
+// disconnected({ willAttemptReconnect: boolean }) {
+// // Called when the client has disconnected with the server.
+// // The object will have an `willAttemptReconnect` property which
+// // says whether the client has the intention of attempting
+// // to reconnect.
+// },
//
-// appear: ->
-// @perform 'appear', appearing_on: @appearingOn()
+// appear() {
+// this.perform('appear', {appearing_on: this.appearingOn()})
+// },
//
-// away: ->
-// @perform 'away'
+// away() {
+// this.perform('away')
+// },
//
-// appearingOn: ->
-// $('main').data 'appearing-on'
+// appearingOn() {
+// $('main').data('appearing-on')
+// }
+// })
//
// The methods #appear and #away forward their intent to the remote AppearanceChannel instance on the server
-// by calling the `@perform` method with the first parameter being the action (which maps to AppearanceChannel#appear/away).
+// by calling the `perform` method with the first parameter being the action (which maps to AppearanceChannel#appear/away).
// The second parameter is a hash that'll get JSON encoded and made available on the server in the data parameter.
//
// This is how the server component would look:
@@ -55,32 +54,27 @@
// end
//
// The "AppearanceChannel" name is automatically mapped between the client-side subscription creation and the server-side Ruby class name.
-// The AppearanceChannel#appear/away public methods are exposed automatically to client-side invocation through the @perform method.
-(function() {
- let extend = undefined
- const Cls = (ActionCable.Subscription = class Subscription {
- static initClass() {
-
- extend = function(object, properties) {
- if (properties != null) {
- for (let key in properties) {
- const value = properties[key]
- object[key] = value
- }
- }
- return object
+// The AppearanceChannel#appear/away public methods are exposed automatically to client-side invocation through the perform method.
+ActionCable.Subscription = (function() {
+ const extend = function(object, properties) {
+ if (properties != null) {
+ for (let key in properties) {
+ const value = properties[key]
+ object[key] = value
}
}
- constructor(consumer, params, mixin) {
+ return object
+ }
+
+ class Subscription {
+ constructor(consumer, params = {}, mixin) {
this.consumer = consumer
- if (params == null) { params = {} }
this.identifier = JSON.stringify(params)
extend(this, mixin)
}
// Perform a channel action with the optional data passed as an attribute
- perform(action, data) {
- if (data == null) { data = {} }
+ perform(action, data = {}) {
data.action = action
return this.send(data)
}
@@ -92,7 +86,7 @@
unsubscribe() {
return this.consumer.subscriptions.remove(this)
}
- })
- Cls.initClass()
- return Cls
+ }
+
+ return Subscription
})()
diff --git a/actioncable/app/javascript/action_cable/subscriptions.js b/actioncable/app/javascript/action_cable/subscriptions.js
index 105dc51b56..65bdcc4ece 100644
--- a/actioncable/app/javascript/action_cable/subscriptions.js
+++ b/actioncable/app/javascript/action_cable/subscriptions.js
@@ -1,16 +1,9 @@
-/*
- * decaffeinate suggestions:
- * DS101: Remove unnecessary use of Array.from
- * DS102: Remove unnecessary code created because of implicit returns
- * DS205: Consider reworking code to avoid use of IIFEs
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
- */
// 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:
//
-// @App = {}
-// App.cable = ActionCable.createConsumer "ws://example.com/accounts/1"
-// App.appearance = App.cable.subscriptions.create "AppearanceChannel"
+// App = {}
+// App.cable = ActionCable.createConsumer("ws://example.com/accounts/1")
+// App.appearance = App.cable.subscriptions.create("AppearanceChannel")
//
// For more details on how you'd configure an actual channel subscription, see ActionCable.Subscription.
ActionCable.Subscriptions = class Subscriptions {
@@ -45,34 +38,30 @@ ActionCable.Subscriptions = class Subscriptions {
}
reject(identifier) {
- return (() => {
- const result = []
- for (let subscription of Array.from(this.findAll(identifier))) {
- this.forget(subscription)
- this.notify(subscription, "rejected")
- result.push(subscription)
- }
- return result
- })()
+ return this.findAll(identifier).map((subscription) => {
+ this.forget(subscription)
+ this.notify(subscription, "rejected")
+ return subscription
+ })
}
forget(subscription) {
- this.subscriptions = (Array.from(this.subscriptions).filter((s) => s !== subscription))
+ this.subscriptions = (this.subscriptions.filter((s) => s !== subscription))
return subscription
}
findAll(identifier) {
- return Array.from(this.subscriptions).filter((s) => s.identifier === identifier)
+ return this.subscriptions.filter((s) => s.identifier === identifier)
}
reload() {
- return Array.from(this.subscriptions).map((subscription) =>
+ return this.subscriptions.map((subscription) =>
this.sendCommand(subscription, "subscribe"))
}
notifyAll(callbackName, ...args) {
- return Array.from(this.subscriptions).map((subscription) =>
- this.notify(subscription, callbackName, ...Array.from(args)))
+ return this.subscriptions.map((subscription) =>
+ this.notify(subscription, callbackName, ...args))
}
notify(subscription, callbackName, ...args) {
@@ -83,13 +72,8 @@ ActionCable.Subscriptions = class Subscriptions {
subscriptions = [subscription]
}
- return (() => {
- const result = []
- for (subscription of Array.from(subscriptions)) {
- result.push((typeof subscription[callbackName] === "function" ? subscription[callbackName](...Array.from(args || [])) : undefined))
- }
- return result
- })()
+ return subscriptions.map((subscription) =>
+ (typeof subscription[callbackName] === "function" ? subscription[callbackName](...args) : undefined))
}
sendCommand(subscription, command) {