Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

actioncable

Package Overview
Dependencies
Maintainers
4
Versions
72
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

actioncable - npm Package Compare versions

Comparing version 5.0.1 to 5.1.0-beta1

127

CHANGELOG.md

@@ -1,32 +0,16 @@

## Rails 5.0.1 (December 21, 2016) ##
## Rails 5.1.0.beta1 (February 23, 2017) ##
* No changes.
* Redis subscription adapters now support `channel_prefix` option in `cable.yml`
Avoids channel name collisions when multiple apps use the same Redis server.
## Rails 5.0.1.rc2 (December 10, 2016) ##
*Chad Ingram*
* No changes.
## Rails 5.0.1.rc1 (December 01, 2016) ##
* Permit same-origin connections by default.
New option `config.action_cable.allow_same_origin_as_host = false`
to disable.
Added new option `config.action_cable.allow_same_origin_as_host = false`
to disable this behaviour.
*Dávid Halász*, *Matthew Draper*
* Fixed and added a workaround to avoid race condition, when one
thread closed the IO, when an another thread was still trying read
from IO on a connection.
*Matthew Draper*
* Shutdown pubsub connection before classes are reloaded, to avoid
hangups caused by pubsub still holding reference to Active Record
connection from the pool, and Active Record trying to cleanup the pool.
*Jon Moss*
* Prevent race where the client could receive and act upon a

@@ -40,3 +24,3 @@ subscription confirmation before the channel's `subscribed` method

* Buffer writes to websocket connections, to avoid blocking threads
* Buffer now writes to WebSocket connections, to avoid blocking threads
that could be doing more useful things.

@@ -46,14 +30,11 @@

* Invocation of channel action is now prevented, if subscription
connection was rejected.
* Protect against concurrent writes to a WebSocket connection from
multiple threads; the underlying OS write is not always threadsafe.
Fixes #23757.
*Tinco Andringa*
*Jon Moss*
* Add `ActiveSupport::Notifications` hook to `Broadcaster#broadcast`.
* Protect against concurrent writes to a websocket connection from
multiple threads; the underlying OS write is not always threadsafe.
*Matthew Wear*
*Tinco Andringa*
* Close hijacked socket when connection is shut down.

@@ -66,84 +47,2 @@

## Rails 5.0.0 (June 30, 2016) ##
* Fix development reloading support: new cable connections are now correctly
dispatched to the reloaded channel class, instead of using a cached reference
to the originally-loaded version.
*Matthew Draper*
* WebSocket protocol negotiation.
Introduces an Action Cable protocol version that moves independently
of and, hopefully, more slowly than Action Cable itself. Client sockets
negotiate a protocol with the Cable server using WebSockets' native
subprotocol support:
* https://tools.ietf.org/html/rfc6455#section-1.9
* https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#Subprotocols
If they can't negotiate a compatible protocol (usually due to upgrading
the Cable server with a browser still running old JavaScript) then the
client knows to disconnect, cease retrying, and tell the app that it hit
a protocol mismatch.
This allows us to evolve the Action Cable message format, handshaking,
pings, acknowledgements, and more without breaking older clients'
expectations of server behavior.
*Daniel Rhodes*
* Pubsub: automatic stream decoding.
stream_for @room, coder: ActiveSupport::JSON do |message|
# `message` is a Ruby hash here instead of a JSON string
The `coder` must respond to `#decode`. Defaults to `coder: nil`
which skips decoding entirely.
*Jeremy Daer*
* Add ActiveSupport::Notifications to ActionCable::Channel.
*Matthew Wear*
* Safely support autoloading and class unloading, by preventing concurrent
loads, and disconnecting all cables during reload.
*Matthew Draper*
* Ensure ActionCable behaves correctly for non-string queue names.
*Jay Hayes*
* Added `em_redis_connector` and `redis_connector` to
`ActionCable::SubscriptionAdapter::EventedRedis` and added `redis_connector`
to `ActionCable::SubscriptionAdapter::Redis`, so you can overwrite with your
own initializers. This is used when you want to use different-than-standard
Redis adapters, like for Makara distributed Redis.
*DHH*
* Support PostgreSQL pubsub adapter.
*Jon Moss*
* Remove EventMachine dependency.
*Matthew Draper*
* Remove Celluloid dependency.
*Mike Perham*
* Create notion of an `ActionCable::SubscriptionAdapter`.
Separate out Redis functionality into
`ActionCable::SubscriptionAdapter::Redis`, and add a
PostgreSQL adapter as well. Configuration file for
ActionCable was changed from`config/redis/cable.yml` to
`config/cable.yml`.
*Jon Moss*
* Added to Rails!
*DHH*
Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/actioncable/CHANGELOG.md) for previous changes.

1021

lib/assets/compiled/action_cable.js
(function() {
(function() {
(function() {
var slice = [].slice;
var slice = [].slice;
this.ActionCable = {
INTERNAL: {
"message_types": {
"welcome": "welcome",
"ping": "ping",
"confirmation": "confirm_subscription",
"rejection": "reject_subscription"
},
"default_mount_path": "/cable",
"protocols": ["actioncable-v1-json", "actioncable-unsupported"]
},
createConsumer: function(url) {
var ref;
if (url == null) {
url = (ref = this.getConfig("url")) != null ? ref : this.INTERNAL.default_mount_path;
}
return new ActionCable.Consumer(this.createWebSocketURL(url));
},
getConfig: function(name) {
var element;
element = document.head.querySelector("meta[name='action-cable-" + name + "']");
return element != null ? element.getAttribute("content") : void 0;
},
createWebSocketURL: function(url) {
var a;
if (url && !/^wss?:/i.test(url)) {
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() {
return this.debugging = true;
},
stopDebugging: function() {
return this.debugging = null;
},
log: function() {
var messages;
messages = 1 <= arguments.length ? slice.call(arguments, 0) : [];
if (this.debugging) {
messages.push(Date.now());
return console.log.apply(console, ["[ActionCable]"].concat(slice.call(messages)));
}
}
};
this.ActionCable = {
INTERNAL: {
"message_types": {
"welcome": "welcome",
"ping": "ping",
"confirmation": "confirm_subscription",
"rejection": "reject_subscription"
},
"default_mount_path": "/cable",
"protocols": ["actioncable-v1-json", "actioncable-unsupported"]
},
WebSocket: window.WebSocket,
logger: window.console,
createConsumer: function(url) {
var ref;
if (url == null) {
url = (ref = this.getConfig("url")) != null ? ref : this.INTERNAL.default_mount_path;
}
return new ActionCable.Consumer(this.createWebSocketURL(url));
},
getConfig: function(name) {
var element;
element = document.head.querySelector("meta[name='action-cable-" + name + "']");
return element != null ? element.getAttribute("content") : void 0;
},
createWebSocketURL: function(url) {
var a;
if (url && !/^wss?:/i.test(url)) {
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() {
return this.debugging = true;
},
stopDebugging: function() {
return this.debugging = null;
},
log: function() {
var messages, ref;
messages = 1 <= arguments.length ? slice.call(arguments, 0) : [];
if (this.debugging) {
messages.push(Date.now());
return (ref = this.logger).log.apply(ref, ["[ActionCable]"].concat(slice.call(messages)));
}
}
};
}).call(this);
}).call(this);
}).call(this);
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
var ActionCable = this.ActionCable;
ActionCable.ConnectionMonitor = (function() {
var clamp, now, secondsSince;
(function() {
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
ConnectionMonitor.pollInterval = {
min: 3,
max: 30
};
ActionCable.ConnectionMonitor = (function() {
var clamp, now, secondsSince;
ConnectionMonitor.staleThreshold = 6;
ConnectionMonitor.pollInterval = {
min: 3,
max: 30
};
function ConnectionMonitor(connection) {
this.connection = connection;
this.visibilityDidChange = bind(this.visibilityDidChange, this);
this.reconnectAttempts = 0;
}
ConnectionMonitor.staleThreshold = 6;
ConnectionMonitor.prototype.start = function() {
if (!this.isRunning()) {
this.startedAt = now();
delete this.stoppedAt;
this.startPolling();
document.addEventListener("visibilitychange", this.visibilityDidChange);
return ActionCable.log("ConnectionMonitor started. pollInterval = " + (this.getPollInterval()) + " ms");
}
};
function ConnectionMonitor(connection) {
this.connection = connection;
this.visibilityDidChange = bind(this.visibilityDidChange, this);
this.reconnectAttempts = 0;
}
ConnectionMonitor.prototype.stop = function() {
if (this.isRunning()) {
this.stoppedAt = now();
this.stopPolling();
document.removeEventListener("visibilitychange", this.visibilityDidChange);
return ActionCable.log("ConnectionMonitor stopped");
}
};
ConnectionMonitor.prototype.start = function() {
if (!this.isRunning()) {
this.startedAt = now();
delete this.stoppedAt;
this.startPolling();
document.addEventListener("visibilitychange", this.visibilityDidChange);
return ActionCable.log("ConnectionMonitor started. pollInterval = " + (this.getPollInterval()) + " ms");
}
};
ConnectionMonitor.prototype.isRunning = function() {
return (this.startedAt != null) && (this.stoppedAt == null);
};
ConnectionMonitor.prototype.stop = function() {
if (this.isRunning()) {
this.stoppedAt = now();
this.stopPolling();
document.removeEventListener("visibilitychange", this.visibilityDidChange);
return ActionCable.log("ConnectionMonitor stopped");
}
};
ConnectionMonitor.prototype.recordPing = function() {
return this.pingedAt = now();
};
ConnectionMonitor.prototype.isRunning = function() {
return (this.startedAt != null) && (this.stoppedAt == null);
};
ConnectionMonitor.prototype.recordConnect = function() {
this.reconnectAttempts = 0;
this.recordPing();
delete this.disconnectedAt;
return ActionCable.log("ConnectionMonitor recorded connect");
};
ConnectionMonitor.prototype.recordPing = function() {
return this.pingedAt = now();
};
ConnectionMonitor.prototype.recordDisconnect = function() {
this.disconnectedAt = now();
return ActionCable.log("ConnectionMonitor recorded disconnect");
};
ConnectionMonitor.prototype.recordConnect = function() {
this.reconnectAttempts = 0;
this.recordPing();
delete this.disconnectedAt;
return ActionCable.log("ConnectionMonitor recorded connect");
};
ConnectionMonitor.prototype.startPolling = function() {
this.stopPolling();
return this.poll();
};
ConnectionMonitor.prototype.recordDisconnect = function() {
this.disconnectedAt = now();
return ActionCable.log("ConnectionMonitor recorded disconnect");
};
ConnectionMonitor.prototype.stopPolling = function() {
return clearTimeout(this.pollTimeout);
};
ConnectionMonitor.prototype.startPolling = function() {
this.stopPolling();
return this.poll();
ConnectionMonitor.prototype.poll = function() {
return this.pollTimeout = setTimeout((function(_this) {
return function() {
_this.reconnectIfStale();
return _this.poll();
};
})(this), this.getPollInterval());
};
ConnectionMonitor.prototype.stopPolling = function() {
return clearTimeout(this.pollTimeout);
};
ConnectionMonitor.prototype.getPollInterval = function() {
var interval, max, min, ref;
ref = this.constructor.pollInterval, min = ref.min, max = ref.max;
interval = 5 * Math.log(this.reconnectAttempts + 1);
return Math.round(clamp(interval, min, max) * 1000);
};
ConnectionMonitor.prototype.poll = function() {
return this.pollTimeout = setTimeout((function(_this) {
return function() {
_this.reconnectIfStale();
return _this.poll();
};
})(this), this.getPollInterval());
};
ConnectionMonitor.prototype.reconnectIfStale = function() {
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()) {
return ActionCable.log("ConnectionMonitor skipping reopening recent disconnect");
} else {
ActionCable.log("ConnectionMonitor reopening");
return this.connection.reopen();
}
}
};
ConnectionMonitor.prototype.getPollInterval = function() {
var interval, max, min, ref;
ref = this.constructor.pollInterval, min = ref.min, max = ref.max;
interval = 5 * Math.log(this.reconnectAttempts + 1);
return Math.round(clamp(interval, min, max) * 1000);
};
ConnectionMonitor.prototype.connectionIsStale = function() {
var ref;
return secondsSince((ref = this.pingedAt) != null ? ref : this.startedAt) > this.constructor.staleThreshold;
};
ConnectionMonitor.prototype.reconnectIfStale = function() {
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()) {
return ActionCable.log("ConnectionMonitor skipping reopening recent disconnect");
} else {
ActionCable.log("ConnectionMonitor reopening");
return this.connection.reopen();
ConnectionMonitor.prototype.disconnectedRecently = function() {
return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold;
};
ConnectionMonitor.prototype.visibilityDidChange = function() {
if (document.visibilityState === "visible") {
return setTimeout((function(_this) {
return function() {
if (_this.connectionIsStale() || !_this.connection.isOpen()) {
ActionCable.log("ConnectionMonitor reopening stale connection on visibilitychange. visbilityState = " + document.visibilityState);
return _this.connection.reopen();
}
}
};
};
})(this), 200);
}
};
ConnectionMonitor.prototype.connectionIsStale = function() {
var ref;
return secondsSince((ref = this.pingedAt) != null ? ref : this.startedAt) > this.constructor.staleThreshold;
};
now = function() {
return new Date().getTime();
};
ConnectionMonitor.prototype.disconnectedRecently = function() {
return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold;
};
secondsSince = function(time) {
return (now() - time) / 1000;
};
ConnectionMonitor.prototype.visibilityDidChange = function() {
if (document.visibilityState === "visible") {
return setTimeout((function(_this) {
return function() {
if (_this.connectionIsStale() || !_this.connection.isOpen()) {
ActionCable.log("ConnectionMonitor reopening stale connection on visibilitychange. visbilityState = " + document.visibilityState);
return _this.connection.reopen();
}
};
})(this), 200);
}
};
clamp = function(number, min, max) {
return Math.max(min, Math.min(max, number));
};
now = function() {
return new Date().getTime();
};
return ConnectionMonitor;
secondsSince = function(time) {
return (now() - time) / 1000;
};
})();
clamp = function(number, min, max) {
return Math.max(min, Math.min(max, number));
};
}).call(this);
(function() {
var i, message_types, protocols, ref, supportedProtocols, unsupportedProtocol,
slice = [].slice,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
return ConnectionMonitor;
ref = ActionCable.INTERNAL, message_types = ref.message_types, protocols = ref.protocols;
})();
supportedProtocols = 2 <= protocols.length ? slice.call(protocols, 0, i = protocols.length - 1) : (i = 0, []), unsupportedProtocol = protocols[i++];
}).call(this);
(function() {
var i, message_types, protocols, ref, supportedProtocols, unsupportedProtocol,
slice = [].slice,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
ActionCable.Connection = (function() {
Connection.reopenDelay = 500;
ref = ActionCable.INTERNAL, message_types = ref.message_types, protocols = ref.protocols;
function Connection(consumer) {
this.consumer = consumer;
this.open = bind(this.open, this);
this.subscriptions = this.consumer.subscriptions;
this.monitor = new ActionCable.ConnectionMonitor(this);
this.disconnected = true;
}
supportedProtocols = 2 <= protocols.length ? slice.call(protocols, 0, i = protocols.length - 1) : (i = 0, []), unsupportedProtocol = protocols[i++];
Connection.prototype.send = function(data) {
if (this.isOpen()) {
this.webSocket.send(JSON.stringify(data));
return true;
} else {
return false;
}
};
ActionCable.Connection = (function() {
Connection.reopenDelay = 500;
function Connection(consumer) {
this.consumer = consumer;
this.open = bind(this.open, this);
this.subscriptions = this.consumer.subscriptions;
this.monitor = new ActionCable.ConnectionMonitor(this);
this.disconnected = true;
Connection.prototype.open = function() {
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;
}
};
Connection.prototype.send = function(data) {
if (this.isOpen()) {
this.webSocket.send(JSON.stringify(data));
return true;
} else {
return false;
}
};
Connection.prototype.close = function(arg) {
var allowReconnect, ref1;
allowReconnect = (arg != null ? arg : {
allowReconnect: true
}).allowReconnect;
if (!allowReconnect) {
this.monitor.stop();
}
if (this.isActive()) {
return (ref1 = this.webSocket) != null ? ref1.close() : void 0;
}
};
Connection.prototype.open = function() {
if (this.isActive()) {
ActionCable.log("Attempted to open WebSocket, but existing socket is " + (this.getState()));
throw new Error("Existing connection must be closed before opening");
} else {
ActionCable.log("Opening WebSocket, current state is " + (this.getState()) + ", subprotocols: " + protocols);
if (this.webSocket != null) {
this.uninstallEventHandlers();
}
this.webSocket = new WebSocket(this.consumer.url, protocols);
this.installEventHandlers();
this.monitor.start();
return true;
}
};
Connection.prototype.reopen = function() {
var error;
ActionCable.log("Reopening WebSocket, current state is " + (this.getState()));
if (this.isActive()) {
try {
return this.close();
} catch (error1) {
error = error1;
return 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();
}
};
Connection.prototype.close = function(arg) {
var allowReconnect, ref1;
allowReconnect = (arg != null ? arg : {
allowReconnect: true
}).allowReconnect;
if (!allowReconnect) {
this.monitor.stop();
}
if (this.isActive()) {
return (ref1 = this.webSocket) != null ? ref1.close() : void 0;
}
};
Connection.prototype.getProtocol = function() {
var ref1;
return (ref1 = this.webSocket) != null ? ref1.protocol : void 0;
};
Connection.prototype.reopen = function() {
var error, error1;
ActionCable.log("Reopening WebSocket, current state is " + (this.getState()));
if (this.isActive()) {
try {
return this.close();
} catch (error1) {
error = error1;
return 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();
}
};
Connection.prototype.isOpen = function() {
return this.isState("open");
};
Connection.prototype.getProtocol = function() {
var ref1;
return (ref1 = this.webSocket) != null ? ref1.protocol : void 0;
};
Connection.prototype.isActive = function() {
return this.isState("open", "connecting");
};
Connection.prototype.isOpen = function() {
return this.isState("open");
};
Connection.prototype.isProtocolSupported = function() {
var ref1;
return ref1 = this.getProtocol(), indexOf.call(supportedProtocols, ref1) >= 0;
};
Connection.prototype.isActive = function() {
return this.isState("open", "connecting");
};
Connection.prototype.isState = function() {
var ref1, states;
states = 1 <= arguments.length ? slice.call(arguments, 0) : [];
return ref1 = this.getState(), indexOf.call(states, ref1) >= 0;
};
Connection.prototype.isProtocolSupported = function() {
var ref1;
return ref1 = this.getProtocol(), indexOf.call(supportedProtocols, ref1) >= 0;
};
Connection.prototype.getState = function() {
var ref1, state, value;
for (state in WebSocket) {
value = WebSocket[state];
if (value === ((ref1 = this.webSocket) != null ? ref1.readyState : void 0)) {
return state.toLowerCase();
}
}
return null;
};
Connection.prototype.isState = function() {
var ref1, states;
states = 1 <= arguments.length ? slice.call(arguments, 0) : [];
return ref1 = this.getState(), indexOf.call(states, ref1) >= 0;
};
Connection.prototype.installEventHandlers = function() {
var eventName, handler;
for (eventName in this.events) {
handler = this.events[eventName].bind(this);
this.webSocket["on" + eventName] = handler;
}
};
Connection.prototype.getState = function() {
var ref1, state, value;
for (state in WebSocket) {
value = WebSocket[state];
if (value === ((ref1 = this.webSocket) != null ? ref1.readyState : void 0)) {
return state.toLowerCase();
}
}
return null;
};
Connection.prototype.uninstallEventHandlers = function() {
var eventName;
for (eventName in this.events) {
this.webSocket["on" + eventName] = function() {};
}
};
Connection.prototype.installEventHandlers = function() {
var eventName, handler;
for (eventName in this.events) {
handler = this.events[eventName].bind(this);
this.webSocket["on" + eventName] = handler;
}
};
Connection.prototype.events = {
message: function(event) {
var identifier, message, ref1, type;
if (!this.isProtocolSupported()) {
return;
}
ref1 = JSON.parse(event.data), identifier = ref1.identifier, message = ref1.message, type = ref1.type;
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: function() {
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: function(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: function() {
return ActionCable.log("WebSocket onerror event");
}
};
Connection.prototype.uninstallEventHandlers = function() {
var eventName;
for (eventName in this.events) {
this.webSocket["on" + eventName] = function() {};
}
};
return Connection;
Connection.prototype.events = {
message: function(event) {
var identifier, message, ref1, type;
if (!this.isProtocolSupported()) {
return;
}
ref1 = JSON.parse(event.data), identifier = ref1.identifier, message = ref1.message, type = ref1.type;
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: function() {
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: function(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: function() {
return ActionCable.log("WebSocket onerror event");
}
};
})();
return Connection;
}).call(this);
(function() {
var slice = [].slice;
})();
ActionCable.Subscriptions = (function() {
function Subscriptions(consumer) {
this.consumer = consumer;
this.subscriptions = [];
}
}).call(this);
(function() {
var slice = [].slice;
Subscriptions.prototype.create = function(channelName, mixin) {
var channel, params, subscription;
channel = channelName;
params = typeof channel === "object" ? channel : {
channel: channel
};
subscription = new ActionCable.Subscription(this.consumer, params, mixin);
return this.add(subscription);
};
ActionCable.Subscriptions = (function() {
function Subscriptions(consumer) {
this.consumer = consumer;
this.subscriptions = [];
}
Subscriptions.prototype.add = function(subscription) {
this.subscriptions.push(subscription);
this.consumer.ensureActiveConnection();
this.notify(subscription, "initialized");
this.sendCommand(subscription, "subscribe");
return subscription;
};
Subscriptions.prototype.create = function(channelName, mixin) {
var channel, params, subscription;
channel = channelName;
params = typeof channel === "object" ? channel : {
channel: channel
};
subscription = new ActionCable.Subscription(this.consumer, params, mixin);
return this.add(subscription);
};
Subscriptions.prototype.remove = function(subscription) {
this.forget(subscription);
if (!this.findAll(subscription.identifier).length) {
this.sendCommand(subscription, "unsubscribe");
}
return subscription;
};
Subscriptions.prototype.add = function(subscription) {
this.subscriptions.push(subscription);
this.consumer.ensureActiveConnection();
this.notify(subscription, "initialized");
this.sendCommand(subscription, "subscribe");
return subscription;
};
Subscriptions.prototype.reject = function(identifier) {
var i, len, ref, results, subscription;
ref = this.findAll(identifier);
results = [];
for (i = 0, len = ref.length; i < len; i++) {
subscription = ref[i];
this.forget(subscription);
this.notify(subscription, "rejected");
results.push(subscription);
}
return results;
};
Subscriptions.prototype.remove = function(subscription) {
this.forget(subscription);
if (!this.findAll(subscription.identifier).length) {
this.sendCommand(subscription, "unsubscribe");
Subscriptions.prototype.forget = function(subscription) {
var s;
this.subscriptions = (function() {
var i, len, ref, results;
ref = this.subscriptions;
results = [];
for (i = 0, len = ref.length; i < len; i++) {
s = ref[i];
if (s !== subscription) {
results.push(s);
}
return subscription;
};
}
return results;
}).call(this);
return subscription;
};
Subscriptions.prototype.reject = function(identifier) {
var i, len, ref, results, subscription;
ref = this.findAll(identifier);
results = [];
for (i = 0, len = ref.length; i < len; i++) {
subscription = ref[i];
this.forget(subscription);
this.notify(subscription, "rejected");
results.push(subscription);
}
return results;
};
Subscriptions.prototype.findAll = function(identifier) {
var i, len, ref, results, s;
ref = this.subscriptions;
results = [];
for (i = 0, len = ref.length; i < len; i++) {
s = ref[i];
if (s.identifier === identifier) {
results.push(s);
}
}
return results;
};
Subscriptions.prototype.forget = function(subscription) {
var s;
this.subscriptions = (function() {
var i, len, ref, results;
ref = this.subscriptions;
results = [];
for (i = 0, len = ref.length; i < len; i++) {
s = ref[i];
if (s !== subscription) {
results.push(s);
}
}
return results;
}).call(this);
return subscription;
};
Subscriptions.prototype.reload = function() {
var i, len, ref, results, subscription;
ref = this.subscriptions;
results = [];
for (i = 0, len = ref.length; i < len; i++) {
subscription = ref[i];
results.push(this.sendCommand(subscription, "subscribe"));
}
return results;
};
Subscriptions.prototype.findAll = function(identifier) {
var i, len, ref, results, s;
ref = this.subscriptions;
results = [];
for (i = 0, len = ref.length; i < len; i++) {
s = ref[i];
if (s.identifier === identifier) {
results.push(s);
}
}
return results;
};
Subscriptions.prototype.notifyAll = function() {
var args, callbackName, i, len, ref, results, subscription;
callbackName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
ref = this.subscriptions;
results = [];
for (i = 0, len = ref.length; i < len; i++) {
subscription = ref[i];
results.push(this.notify.apply(this, [subscription, callbackName].concat(slice.call(args))));
}
return results;
};
Subscriptions.prototype.reload = function() {
var i, len, ref, results, subscription;
ref = this.subscriptions;
results = [];
for (i = 0, len = ref.length; i < len; i++) {
subscription = ref[i];
results.push(this.sendCommand(subscription, "subscribe"));
}
return results;
};
Subscriptions.prototype.notify = function() {
var args, callbackName, i, len, results, subscription, subscriptions;
subscription = arguments[0], callbackName = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
if (typeof subscription === "string") {
subscriptions = this.findAll(subscription);
} else {
subscriptions = [subscription];
}
results = [];
for (i = 0, len = subscriptions.length; i < len; i++) {
subscription = subscriptions[i];
results.push(typeof subscription[callbackName] === "function" ? subscription[callbackName].apply(subscription, args) : void 0);
}
return results;
};
Subscriptions.prototype.notifyAll = function() {
var args, callbackName, i, len, ref, results, subscription;
callbackName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
ref = this.subscriptions;
results = [];
for (i = 0, len = ref.length; i < len; i++) {
subscription = ref[i];
results.push(this.notify.apply(this, [subscription, callbackName].concat(slice.call(args))));
}
return results;
};
Subscriptions.prototype.sendCommand = function(subscription, command) {
var identifier;
identifier = subscription.identifier;
return this.consumer.send({
command: command,
identifier: identifier
});
};
Subscriptions.prototype.notify = function() {
var args, callbackName, i, len, results, subscription, subscriptions;
subscription = arguments[0], callbackName = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
if (typeof subscription === "string") {
subscriptions = this.findAll(subscription);
} else {
subscriptions = [subscription];
}
results = [];
for (i = 0, len = subscriptions.length; i < len; i++) {
subscription = subscriptions[i];
results.push(typeof subscription[callbackName] === "function" ? subscription[callbackName].apply(subscription, args) : void 0);
}
return results;
};
return Subscriptions;
Subscriptions.prototype.sendCommand = function(subscription, command) {
var identifier;
identifier = subscription.identifier;
return this.consumer.send({
command: command,
identifier: identifier
});
};
})();
return Subscriptions;
}).call(this);
(function() {
ActionCable.Subscription = (function() {
var extend;
})();
function Subscription(consumer, params, mixin) {
this.consumer = consumer;
if (params == null) {
params = {};
}
this.identifier = JSON.stringify(params);
extend(this, mixin);
}
}).call(this);
(function() {
ActionCable.Subscription = (function() {
var extend;
Subscription.prototype.perform = function(action, data) {
if (data == null) {
data = {};
}
data.action = action;
return this.send(data);
};
function Subscription(consumer, params, mixin) {
this.consumer = consumer;
if (params == null) {
params = {};
}
this.identifier = JSON.stringify(params);
extend(this, mixin);
}
Subscription.prototype.send = function(data) {
return this.consumer.send({
command: "message",
identifier: this.identifier,
data: JSON.stringify(data)
});
};
Subscription.prototype.perform = function(action, data) {
if (data == null) {
data = {};
}
data.action = action;
return this.send(data);
};
Subscription.prototype.unsubscribe = function() {
return this.consumer.subscriptions.remove(this);
};
Subscription.prototype.send = function(data) {
return this.consumer.send({
command: "message",
identifier: this.identifier,
data: JSON.stringify(data)
});
};
extend = function(object, properties) {
var key, value;
if (properties != null) {
for (key in properties) {
value = properties[key];
object[key] = value;
}
}
return object;
};
Subscription.prototype.unsubscribe = function() {
return this.consumer.subscriptions.remove(this);
};
return Subscription;
extend = function(object, properties) {
var key, value;
if (properties != null) {
for (key in properties) {
value = properties[key];
object[key] = value;
}
}
return object;
};
})();
return Subscription;
}).call(this);
(function() {
ActionCable.Consumer = (function() {
function Consumer(url) {
this.url = url;
this.subscriptions = new ActionCable.Subscriptions(this);
this.connection = new ActionCable.Connection(this);
}
})();
Consumer.prototype.send = function(data) {
return this.connection.send(data);
};
}).call(this);
(function() {
ActionCable.Consumer = (function() {
function Consumer(url) {
this.url = url;
this.subscriptions = new ActionCable.Subscriptions(this);
this.connection = new ActionCable.Connection(this);
}
Consumer.prototype.connect = function() {
return this.connection.open();
};
Consumer.prototype.send = function(data) {
return this.connection.send(data);
};
Consumer.prototype.disconnect = function() {
return this.connection.close({
allowReconnect: false
});
};
Consumer.prototype.connect = function() {
return this.connection.open();
};
Consumer.prototype.ensureActiveConnection = function() {
if (!this.connection.isActive()) {
return this.connection.open();
}
};
Consumer.prototype.disconnect = function() {
return this.connection.close({
allowReconnect: false
});
};
return Consumer;
Consumer.prototype.ensureActiveConnection = function() {
if (!this.connection.isActive()) {
return this.connection.open();
}
};
})();
return Consumer;
})();
}).call(this);
}).call(this);
if (typeof module === "object" && module.exports) {
module.exports = ActionCable;
} else if (typeof define === "function" && define.amd) {
define(ActionCable);
}
}).call(this);
{
"name": "actioncable",
"version": "5.0.1",
"version": "5.1.0-beta1",
"description": "WebSocket framework for Ruby on Rails.",

@@ -5,0 +5,0 @@ "main": "lib/assets/compiled/action_cable.js",

@@ -10,3 +10,2 @@ # Action Cable – Integrated WebSockets for Rails

## Terminology

@@ -56,3 +55,3 @@

protected
private
def find_verified_user

@@ -91,8 +90,13 @@ if current_user = User.find_by(id: cookies.signed[:user_id])

```coffeescript
# app/assets/javascripts/cable.coffee
#= require action_cable
```js
// app/assets/javascripts/cable.js
//= require action_cable
//= require_self
//= require_tree ./channels
@App = {}
App.cable = ActionCable.createConsumer("ws://cable.example.com")
(function() {
this.App || (this.App = {});
App.cable = ActionCable.createConsumer("ws://cable.example.com");
}).call(this);
```

@@ -169,3 +173,3 @@

install: ->
$(document).on "page:change.appearance", =>
$(document).on "turbolinks:load.appearance", =>
@appear()

@@ -300,5 +304,4 @@

See the [rails/actioncable-examples](http://github.com/rails/actioncable-examples) repository for a full example of how to setup Action Cable in a Rails app, and how to add channels.
See the [rails/actioncable-examples](https://github.com/rails/actioncable-examples) repository for a full example of how to setup Action Cable in a Rails app, and how to add channels.
## Configuration

@@ -391,7 +394,7 @@

The other common option to configure is the log tags applied to the per-connection logger. Here's close to what we're using in Basecamp:
The other common option to configure is the log tags applied to the per-connection logger. Here's an example that uses the user account id if available, else "no-account" while tagging:
```ruby
Rails.application.config.action_cable.log_tags = [
-> request { request.env['bc.account_id'] || "no-account" },
config.action_cable.log_tags = [
-> request { request.env['user_account_id'] || "no-account" },
:action_cable,

@@ -422,3 +425,3 @@ -> request { request.uuid }

Then you start the server using a binstub in bin/cable ala:
```
```sh
#!/bin/bash

@@ -445,3 +448,3 @@ bundle exec puma -p 28080 cable/config.ru

Beware that currently the cable server will _not_ auto-reload any changes in the framework. As we've discussed, long-running cable connections mean long-running objects. We don't yet have a way of reloading the classes of those objects in a safe manner. So when you change your channels, or the model your channels use, you must restart the cable server.
Beware that currently, the cable server will _not_ auto-reload any changes in the framework. As we've discussed, long-running cable connections mean long-running objects. We don't yet have a way of reloading the classes of those objects in a safe manner. So when you change your channels, or the model your channels use, you must restart the cable server.

@@ -544,2 +547,11 @@ We'll get all this abstracted properly when the framework is integrated into Rails.

## Download and Installation
The latest version of Action Cable can be installed with [RubyGems](#gem-usage),
or with [npm](#npm-usage).
Source code can be downloaded as part of the Rails project on GitHub
* https://github.com/rails/rails/tree/master/actioncable
## License

@@ -546,0 +558,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc