actioncable 5.0.1 → 6.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +31 -117
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +4 -535
  5. data/app/assets/javascripts/action_cable.js +517 -0
  6. data/lib/action_cable.rb +20 -10
  7. data/lib/action_cable/channel.rb +3 -0
  8. data/lib/action_cable/channel/base.rb +31 -23
  9. data/lib/action_cable/channel/broadcasting.rb +22 -10
  10. data/lib/action_cable/channel/callbacks.rb +4 -2
  11. data/lib/action_cable/channel/naming.rb +5 -2
  12. data/lib/action_cable/channel/periodic_timers.rb +4 -3
  13. data/lib/action_cable/channel/streams.rb +39 -11
  14. data/lib/action_cable/channel/test_case.rb +310 -0
  15. data/lib/action_cable/connection.rb +3 -2
  16. data/lib/action_cable/connection/authorization.rb +8 -6
  17. data/lib/action_cable/connection/base.rb +34 -26
  18. data/lib/action_cable/connection/client_socket.rb +20 -18
  19. data/lib/action_cable/connection/identification.rb +5 -4
  20. data/lib/action_cable/connection/internal_channel.rb +4 -2
  21. data/lib/action_cable/connection/message_buffer.rb +3 -2
  22. data/lib/action_cable/connection/stream.rb +9 -5
  23. data/lib/action_cable/connection/stream_event_loop.rb +4 -2
  24. data/lib/action_cable/connection/subscriptions.rb +14 -13
  25. data/lib/action_cable/connection/tagged_logger_proxy.rb +4 -2
  26. data/lib/action_cable/connection/test_case.rb +234 -0
  27. data/lib/action_cable/connection/web_socket.rb +7 -5
  28. data/lib/action_cable/engine.rb +7 -5
  29. data/lib/action_cable/gem_version.rb +5 -3
  30. data/lib/action_cable/helpers/action_cable_helper.rb +6 -4
  31. data/lib/action_cable/remote_connections.rb +9 -4
  32. data/lib/action_cable/server.rb +2 -1
  33. data/lib/action_cable/server/base.rb +17 -10
  34. data/lib/action_cable/server/broadcasting.rb +9 -3
  35. data/lib/action_cable/server/configuration.rb +21 -22
  36. data/lib/action_cable/server/connections.rb +2 -0
  37. data/lib/action_cable/server/worker.rb +11 -11
  38. data/lib/action_cable/server/worker/active_record_connection_management.rb +2 -0
  39. data/lib/action_cable/subscription_adapter.rb +4 -0
  40. data/lib/action_cable/subscription_adapter/async.rb +3 -1
  41. data/lib/action_cable/subscription_adapter/base.rb +6 -0
  42. data/lib/action_cable/subscription_adapter/channel_prefix.rb +28 -0
  43. data/lib/action_cable/subscription_adapter/inline.rb +2 -0
  44. data/lib/action_cable/subscription_adapter/postgresql.rb +40 -14
  45. data/lib/action_cable/subscription_adapter/redis.rb +19 -11
  46. data/lib/action_cable/subscription_adapter/subscriber_map.rb +3 -1
  47. data/lib/action_cable/subscription_adapter/test.rb +40 -0
  48. data/lib/action_cable/test_case.rb +11 -0
  49. data/lib/action_cable/test_helper.rb +133 -0
  50. data/lib/action_cable/version.rb +3 -1
  51. data/lib/rails/generators/channel/USAGE +5 -6
  52. data/lib/rails/generators/channel/channel_generator.rb +16 -11
  53. data/lib/rails/generators/channel/templates/application_cable/{channel.rb → channel.rb.tt} +0 -0
  54. data/lib/rails/generators/channel/templates/application_cable/{connection.rb → connection.rb.tt} +0 -0
  55. data/lib/rails/generators/channel/templates/{channel.rb → channel.rb.tt} +0 -0
  56. data/lib/rails/generators/channel/templates/{assets/channel.js → javascript/channel.js.tt} +6 -4
  57. data/lib/rails/generators/channel/templates/javascript/consumer.js.tt +6 -0
  58. data/lib/rails/generators/channel/templates/javascript/index.js.tt +5 -0
  59. data/lib/rails/generators/test_unit/channel_generator.rb +20 -0
  60. data/lib/rails/generators/test_unit/templates/channel_test.rb.tt +8 -0
  61. metadata +46 -38
  62. data/lib/action_cable/connection/faye_client_socket.rb +0 -48
  63. data/lib/action_cable/connection/faye_event_loop.rb +0 -44
  64. data/lib/action_cable/subscription_adapter/evented_redis.rb +0 -79
  65. data/lib/assets/compiled/action_cable.js +0 -597
  66. data/lib/rails/generators/channel/templates/assets/cable.js +0 -13
  67. data/lib/rails/generators/channel/templates/assets/channel.coffee +0 -14
@@ -1,48 +0,0 @@
1
- require 'faye/websocket'
2
-
3
- module ActionCable
4
- module Connection
5
- class FayeClientSocket
6
- def initialize(env, event_target, stream_event_loop, protocols)
7
- @env = env
8
- @event_target = event_target
9
- @protocols = protocols
10
-
11
- @faye = nil
12
- end
13
-
14
- def alive?
15
- @faye && @faye.ready_state == Faye::WebSocket::API::OPEN
16
- end
17
-
18
- def transmit(data)
19
- connect
20
- @faye.send data
21
- end
22
-
23
- def close
24
- @faye && @faye.close
25
- end
26
-
27
- def protocol
28
- @faye && @faye.protocol
29
- end
30
-
31
- def rack_response
32
- connect
33
- @faye.rack_response
34
- end
35
-
36
- private
37
- def connect
38
- return if @faye
39
- @faye = Faye::WebSocket.new(@env, @protocols)
40
-
41
- @faye.on(:open) { |event| @event_target.on_open }
42
- @faye.on(:message) { |event| @event_target.on_message(event.data) }
43
- @faye.on(:close) { |event| @event_target.on_close(event.reason, event.code) }
44
- @faye.on(:error) { |event| @event_target.on_error(event.message) }
45
- end
46
- end
47
- end
48
- end
@@ -1,44 +0,0 @@
1
- require 'thread'
2
-
3
- require 'eventmachine'
4
- EventMachine.epoll if EventMachine.epoll?
5
- EventMachine.kqueue if EventMachine.kqueue?
6
-
7
- module ActionCable
8
- module Connection
9
- class FayeEventLoop
10
- @@mutex = Mutex.new
11
-
12
- def timer(interval, &block)
13
- ensure_reactor_running
14
- EMTimer.new(::EM::PeriodicTimer.new(interval, &block))
15
- end
16
-
17
- def post(task = nil, &block)
18
- task ||= block
19
-
20
- ensure_reactor_running
21
- ::EM.next_tick(&task)
22
- end
23
-
24
- private
25
- def ensure_reactor_running
26
- return if EventMachine.reactor_running?
27
- @@mutex.synchronize do
28
- Thread.new { EventMachine.run } unless EventMachine.reactor_running?
29
- Thread.pass until EventMachine.reactor_running?
30
- end
31
- end
32
-
33
- class EMTimer
34
- def initialize(inner)
35
- @inner = inner
36
- end
37
-
38
- def shutdown
39
- @inner.cancel
40
- end
41
- end
42
- end
43
- end
44
- end
@@ -1,79 +0,0 @@
1
- require 'thread'
2
-
3
- gem 'em-hiredis', '~> 0.3.0'
4
- gem 'redis', '~> 3.0'
5
- require 'em-hiredis'
6
- require 'redis'
7
-
8
- EventMachine.epoll if EventMachine.epoll?
9
- EventMachine.kqueue if EventMachine.kqueue?
10
-
11
- module ActionCable
12
- module SubscriptionAdapter
13
- class EventedRedis < Base # :nodoc:
14
- @@mutex = Mutex.new
15
-
16
- # Overwrite this factory method for EventMachine Redis connections if you want to use a different Redis connection library than EM::Hiredis.
17
- # This is needed, for example, when using Makara proxies for distributed Redis.
18
- cattr_accessor(:em_redis_connector) { ->(config) { EM::Hiredis.connect(config[:url]) } }
19
-
20
- # Overwrite this factory method for Redis connections if you want to use a different Redis connection library than Redis.
21
- # This is needed, for example, when using Makara proxies for distributed Redis.
22
- cattr_accessor(:redis_connector) { ->(config) { ::Redis.new(url: config[:url]) } }
23
-
24
- def initialize(*)
25
- super
26
- @redis_connection_for_broadcasts = @redis_connection_for_subscriptions = nil
27
- end
28
-
29
- def broadcast(channel, payload)
30
- redis_connection_for_broadcasts.publish(channel, payload)
31
- end
32
-
33
- def subscribe(channel, message_callback, success_callback = nil)
34
- redis_connection_for_subscriptions.pubsub.subscribe(channel, &message_callback).tap do |result|
35
- result.callback { |reply| success_callback.call } if success_callback
36
- end
37
- end
38
-
39
- def unsubscribe(channel, message_callback)
40
- redis_connection_for_subscriptions.pubsub.unsubscribe_proc(channel, message_callback)
41
- end
42
-
43
- def shutdown
44
- redis_connection_for_subscriptions.pubsub.close_connection
45
- @redis_connection_for_subscriptions = nil
46
- end
47
-
48
- private
49
- def redis_connection_for_subscriptions
50
- ensure_reactor_running
51
- @redis_connection_for_subscriptions || @server.mutex.synchronize do
52
- @redis_connection_for_subscriptions ||= self.class.em_redis_connector.call(@server.config.cable).tap do |redis|
53
- redis.on(:reconnect_failed) do
54
- @logger.error "[ActionCable] Redis reconnect failed."
55
- end
56
-
57
- redis.on(:failed) do
58
- @logger.error "[ActionCable] Redis connection has failed."
59
- end
60
- end
61
- end
62
- end
63
-
64
- def redis_connection_for_broadcasts
65
- @redis_connection_for_broadcasts || @server.mutex.synchronize do
66
- @redis_connection_for_broadcasts ||= self.class.redis_connector.call(@server.config.cable)
67
- end
68
- end
69
-
70
- def ensure_reactor_running
71
- return if EventMachine.reactor_running?
72
- @@mutex.synchronize do
73
- Thread.new { EventMachine.run } unless EventMachine.reactor_running?
74
- Thread.pass until EventMachine.reactor_running?
75
- end
76
- end
77
- end
78
- end
79
- end
@@ -1,597 +0,0 @@
1
- (function() {
2
- (function() {
3
- (function() {
4
- var slice = [].slice;
5
-
6
- this.ActionCable = {
7
- INTERNAL: {
8
- "message_types": {
9
- "welcome": "welcome",
10
- "ping": "ping",
11
- "confirmation": "confirm_subscription",
12
- "rejection": "reject_subscription"
13
- },
14
- "default_mount_path": "/cable",
15
- "protocols": ["actioncable-v1-json", "actioncable-unsupported"]
16
- },
17
- createConsumer: function(url) {
18
- var ref;
19
- if (url == null) {
20
- url = (ref = this.getConfig("url")) != null ? ref : this.INTERNAL.default_mount_path;
21
- }
22
- return new ActionCable.Consumer(this.createWebSocketURL(url));
23
- },
24
- getConfig: function(name) {
25
- var element;
26
- element = document.head.querySelector("meta[name='action-cable-" + name + "']");
27
- return element != null ? element.getAttribute("content") : void 0;
28
- },
29
- createWebSocketURL: function(url) {
30
- var a;
31
- if (url && !/^wss?:/i.test(url)) {
32
- a = document.createElement("a");
33
- a.href = url;
34
- a.href = a.href;
35
- a.protocol = a.protocol.replace("http", "ws");
36
- return a.href;
37
- } else {
38
- return url;
39
- }
40
- },
41
- startDebugging: function() {
42
- return this.debugging = true;
43
- },
44
- stopDebugging: function() {
45
- return this.debugging = null;
46
- },
47
- log: function() {
48
- var messages;
49
- messages = 1 <= arguments.length ? slice.call(arguments, 0) : [];
50
- if (this.debugging) {
51
- messages.push(Date.now());
52
- return console.log.apply(console, ["[ActionCable]"].concat(slice.call(messages)));
53
- }
54
- }
55
- };
56
-
57
- }).call(this);
58
- }).call(this);
59
-
60
- var ActionCable = this.ActionCable;
61
-
62
- (function() {
63
- (function() {
64
- var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
65
-
66
- ActionCable.ConnectionMonitor = (function() {
67
- var clamp, now, secondsSince;
68
-
69
- ConnectionMonitor.pollInterval = {
70
- min: 3,
71
- max: 30
72
- };
73
-
74
- ConnectionMonitor.staleThreshold = 6;
75
-
76
- function ConnectionMonitor(connection) {
77
- this.connection = connection;
78
- this.visibilityDidChange = bind(this.visibilityDidChange, this);
79
- this.reconnectAttempts = 0;
80
- }
81
-
82
- ConnectionMonitor.prototype.start = function() {
83
- if (!this.isRunning()) {
84
- this.startedAt = now();
85
- delete this.stoppedAt;
86
- this.startPolling();
87
- document.addEventListener("visibilitychange", this.visibilityDidChange);
88
- return ActionCable.log("ConnectionMonitor started. pollInterval = " + (this.getPollInterval()) + " ms");
89
- }
90
- };
91
-
92
- ConnectionMonitor.prototype.stop = function() {
93
- if (this.isRunning()) {
94
- this.stoppedAt = now();
95
- this.stopPolling();
96
- document.removeEventListener("visibilitychange", this.visibilityDidChange);
97
- return ActionCable.log("ConnectionMonitor stopped");
98
- }
99
- };
100
-
101
- ConnectionMonitor.prototype.isRunning = function() {
102
- return (this.startedAt != null) && (this.stoppedAt == null);
103
- };
104
-
105
- ConnectionMonitor.prototype.recordPing = function() {
106
- return this.pingedAt = now();
107
- };
108
-
109
- ConnectionMonitor.prototype.recordConnect = function() {
110
- this.reconnectAttempts = 0;
111
- this.recordPing();
112
- delete this.disconnectedAt;
113
- return ActionCable.log("ConnectionMonitor recorded connect");
114
- };
115
-
116
- ConnectionMonitor.prototype.recordDisconnect = function() {
117
- this.disconnectedAt = now();
118
- return ActionCable.log("ConnectionMonitor recorded disconnect");
119
- };
120
-
121
- ConnectionMonitor.prototype.startPolling = function() {
122
- this.stopPolling();
123
- return this.poll();
124
- };
125
-
126
- ConnectionMonitor.prototype.stopPolling = function() {
127
- return clearTimeout(this.pollTimeout);
128
- };
129
-
130
- ConnectionMonitor.prototype.poll = function() {
131
- return this.pollTimeout = setTimeout((function(_this) {
132
- return function() {
133
- _this.reconnectIfStale();
134
- return _this.poll();
135
- };
136
- })(this), this.getPollInterval());
137
- };
138
-
139
- ConnectionMonitor.prototype.getPollInterval = function() {
140
- var interval, max, min, ref;
141
- ref = this.constructor.pollInterval, min = ref.min, max = ref.max;
142
- interval = 5 * Math.log(this.reconnectAttempts + 1);
143
- return Math.round(clamp(interval, min, max) * 1000);
144
- };
145
-
146
- ConnectionMonitor.prototype.reconnectIfStale = function() {
147
- if (this.connectionIsStale()) {
148
- 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");
149
- this.reconnectAttempts++;
150
- if (this.disconnectedRecently()) {
151
- return ActionCable.log("ConnectionMonitor skipping reopening recent disconnect");
152
- } else {
153
- ActionCable.log("ConnectionMonitor reopening");
154
- return this.connection.reopen();
155
- }
156
- }
157
- };
158
-
159
- ConnectionMonitor.prototype.connectionIsStale = function() {
160
- var ref;
161
- return secondsSince((ref = this.pingedAt) != null ? ref : this.startedAt) > this.constructor.staleThreshold;
162
- };
163
-
164
- ConnectionMonitor.prototype.disconnectedRecently = function() {
165
- return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold;
166
- };
167
-
168
- ConnectionMonitor.prototype.visibilityDidChange = function() {
169
- if (document.visibilityState === "visible") {
170
- return setTimeout((function(_this) {
171
- return function() {
172
- if (_this.connectionIsStale() || !_this.connection.isOpen()) {
173
- ActionCable.log("ConnectionMonitor reopening stale connection on visibilitychange. visbilityState = " + document.visibilityState);
174
- return _this.connection.reopen();
175
- }
176
- };
177
- })(this), 200);
178
- }
179
- };
180
-
181
- now = function() {
182
- return new Date().getTime();
183
- };
184
-
185
- secondsSince = function(time) {
186
- return (now() - time) / 1000;
187
- };
188
-
189
- clamp = function(number, min, max) {
190
- return Math.max(min, Math.min(max, number));
191
- };
192
-
193
- return ConnectionMonitor;
194
-
195
- })();
196
-
197
- }).call(this);
198
- (function() {
199
- var i, message_types, protocols, ref, supportedProtocols, unsupportedProtocol,
200
- slice = [].slice,
201
- bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
202
- 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; };
203
-
204
- ref = ActionCable.INTERNAL, message_types = ref.message_types, protocols = ref.protocols;
205
-
206
- supportedProtocols = 2 <= protocols.length ? slice.call(protocols, 0, i = protocols.length - 1) : (i = 0, []), unsupportedProtocol = protocols[i++];
207
-
208
- ActionCable.Connection = (function() {
209
- Connection.reopenDelay = 500;
210
-
211
- function Connection(consumer) {
212
- this.consumer = consumer;
213
- this.open = bind(this.open, this);
214
- this.subscriptions = this.consumer.subscriptions;
215
- this.monitor = new ActionCable.ConnectionMonitor(this);
216
- this.disconnected = true;
217
- }
218
-
219
- Connection.prototype.send = function(data) {
220
- if (this.isOpen()) {
221
- this.webSocket.send(JSON.stringify(data));
222
- return true;
223
- } else {
224
- return false;
225
- }
226
- };
227
-
228
- Connection.prototype.open = function() {
229
- if (this.isActive()) {
230
- ActionCable.log("Attempted to open WebSocket, but existing socket is " + (this.getState()));
231
- throw new Error("Existing connection must be closed before opening");
232
- } else {
233
- ActionCable.log("Opening WebSocket, current state is " + (this.getState()) + ", subprotocols: " + protocols);
234
- if (this.webSocket != null) {
235
- this.uninstallEventHandlers();
236
- }
237
- this.webSocket = new WebSocket(this.consumer.url, protocols);
238
- this.installEventHandlers();
239
- this.monitor.start();
240
- return true;
241
- }
242
- };
243
-
244
- Connection.prototype.close = function(arg) {
245
- var allowReconnect, ref1;
246
- allowReconnect = (arg != null ? arg : {
247
- allowReconnect: true
248
- }).allowReconnect;
249
- if (!allowReconnect) {
250
- this.monitor.stop();
251
- }
252
- if (this.isActive()) {
253
- return (ref1 = this.webSocket) != null ? ref1.close() : void 0;
254
- }
255
- };
256
-
257
- Connection.prototype.reopen = function() {
258
- var error, error1;
259
- ActionCable.log("Reopening WebSocket, current state is " + (this.getState()));
260
- if (this.isActive()) {
261
- try {
262
- return this.close();
263
- } catch (error1) {
264
- error = error1;
265
- return ActionCable.log("Failed to reopen WebSocket", error);
266
- } finally {
267
- ActionCable.log("Reopening WebSocket in " + this.constructor.reopenDelay + "ms");
268
- setTimeout(this.open, this.constructor.reopenDelay);
269
- }
270
- } else {
271
- return this.open();
272
- }
273
- };
274
-
275
- Connection.prototype.getProtocol = function() {
276
- var ref1;
277
- return (ref1 = this.webSocket) != null ? ref1.protocol : void 0;
278
- };
279
-
280
- Connection.prototype.isOpen = function() {
281
- return this.isState("open");
282
- };
283
-
284
- Connection.prototype.isActive = function() {
285
- return this.isState("open", "connecting");
286
- };
287
-
288
- Connection.prototype.isProtocolSupported = function() {
289
- var ref1;
290
- return ref1 = this.getProtocol(), indexOf.call(supportedProtocols, ref1) >= 0;
291
- };
292
-
293
- Connection.prototype.isState = function() {
294
- var ref1, states;
295
- states = 1 <= arguments.length ? slice.call(arguments, 0) : [];
296
- return ref1 = this.getState(), indexOf.call(states, ref1) >= 0;
297
- };
298
-
299
- Connection.prototype.getState = function() {
300
- var ref1, state, value;
301
- for (state in WebSocket) {
302
- value = WebSocket[state];
303
- if (value === ((ref1 = this.webSocket) != null ? ref1.readyState : void 0)) {
304
- return state.toLowerCase();
305
- }
306
- }
307
- return null;
308
- };
309
-
310
- Connection.prototype.installEventHandlers = function() {
311
- var eventName, handler;
312
- for (eventName in this.events) {
313
- handler = this.events[eventName].bind(this);
314
- this.webSocket["on" + eventName] = handler;
315
- }
316
- };
317
-
318
- Connection.prototype.uninstallEventHandlers = function() {
319
- var eventName;
320
- for (eventName in this.events) {
321
- this.webSocket["on" + eventName] = function() {};
322
- }
323
- };
324
-
325
- Connection.prototype.events = {
326
- message: function(event) {
327
- var identifier, message, ref1, type;
328
- if (!this.isProtocolSupported()) {
329
- return;
330
- }
331
- ref1 = JSON.parse(event.data), identifier = ref1.identifier, message = ref1.message, type = ref1.type;
332
- switch (type) {
333
- case message_types.welcome:
334
- this.monitor.recordConnect();
335
- return this.subscriptions.reload();
336
- case message_types.ping:
337
- return this.monitor.recordPing();
338
- case message_types.confirmation:
339
- return this.subscriptions.notify(identifier, "connected");
340
- case message_types.rejection:
341
- return this.subscriptions.reject(identifier);
342
- default:
343
- return this.subscriptions.notify(identifier, "received", message);
344
- }
345
- },
346
- open: function() {
347
- ActionCable.log("WebSocket onopen event, using '" + (this.getProtocol()) + "' subprotocol");
348
- this.disconnected = false;
349
- if (!this.isProtocolSupported()) {
350
- ActionCable.log("Protocol is unsupported. Stopping monitor and disconnecting.");
351
- return this.close({
352
- allowReconnect: false
353
- });
354
- }
355
- },
356
- close: function(event) {
357
- ActionCable.log("WebSocket onclose event");
358
- if (this.disconnected) {
359
- return;
360
- }
361
- this.disconnected = true;
362
- this.monitor.recordDisconnect();
363
- return this.subscriptions.notifyAll("disconnected", {
364
- willAttemptReconnect: this.monitor.isRunning()
365
- });
366
- },
367
- error: function() {
368
- return ActionCable.log("WebSocket onerror event");
369
- }
370
- };
371
-
372
- return Connection;
373
-
374
- })();
375
-
376
- }).call(this);
377
- (function() {
378
- var slice = [].slice;
379
-
380
- ActionCable.Subscriptions = (function() {
381
- function Subscriptions(consumer) {
382
- this.consumer = consumer;
383
- this.subscriptions = [];
384
- }
385
-
386
- Subscriptions.prototype.create = function(channelName, mixin) {
387
- var channel, params, subscription;
388
- channel = channelName;
389
- params = typeof channel === "object" ? channel : {
390
- channel: channel
391
- };
392
- subscription = new ActionCable.Subscription(this.consumer, params, mixin);
393
- return this.add(subscription);
394
- };
395
-
396
- Subscriptions.prototype.add = function(subscription) {
397
- this.subscriptions.push(subscription);
398
- this.consumer.ensureActiveConnection();
399
- this.notify(subscription, "initialized");
400
- this.sendCommand(subscription, "subscribe");
401
- return subscription;
402
- };
403
-
404
- Subscriptions.prototype.remove = function(subscription) {
405
- this.forget(subscription);
406
- if (!this.findAll(subscription.identifier).length) {
407
- this.sendCommand(subscription, "unsubscribe");
408
- }
409
- return subscription;
410
- };
411
-
412
- Subscriptions.prototype.reject = function(identifier) {
413
- var i, len, ref, results, subscription;
414
- ref = this.findAll(identifier);
415
- results = [];
416
- for (i = 0, len = ref.length; i < len; i++) {
417
- subscription = ref[i];
418
- this.forget(subscription);
419
- this.notify(subscription, "rejected");
420
- results.push(subscription);
421
- }
422
- return results;
423
- };
424
-
425
- Subscriptions.prototype.forget = function(subscription) {
426
- var s;
427
- this.subscriptions = (function() {
428
- var i, len, ref, results;
429
- ref = this.subscriptions;
430
- results = [];
431
- for (i = 0, len = ref.length; i < len; i++) {
432
- s = ref[i];
433
- if (s !== subscription) {
434
- results.push(s);
435
- }
436
- }
437
- return results;
438
- }).call(this);
439
- return subscription;
440
- };
441
-
442
- Subscriptions.prototype.findAll = function(identifier) {
443
- var i, len, ref, results, s;
444
- ref = this.subscriptions;
445
- results = [];
446
- for (i = 0, len = ref.length; i < len; i++) {
447
- s = ref[i];
448
- if (s.identifier === identifier) {
449
- results.push(s);
450
- }
451
- }
452
- return results;
453
- };
454
-
455
- Subscriptions.prototype.reload = function() {
456
- var i, len, ref, results, subscription;
457
- ref = this.subscriptions;
458
- results = [];
459
- for (i = 0, len = ref.length; i < len; i++) {
460
- subscription = ref[i];
461
- results.push(this.sendCommand(subscription, "subscribe"));
462
- }
463
- return results;
464
- };
465
-
466
- Subscriptions.prototype.notifyAll = function() {
467
- var args, callbackName, i, len, ref, results, subscription;
468
- callbackName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
469
- ref = this.subscriptions;
470
- results = [];
471
- for (i = 0, len = ref.length; i < len; i++) {
472
- subscription = ref[i];
473
- results.push(this.notify.apply(this, [subscription, callbackName].concat(slice.call(args))));
474
- }
475
- return results;
476
- };
477
-
478
- Subscriptions.prototype.notify = function() {
479
- var args, callbackName, i, len, results, subscription, subscriptions;
480
- subscription = arguments[0], callbackName = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
481
- if (typeof subscription === "string") {
482
- subscriptions = this.findAll(subscription);
483
- } else {
484
- subscriptions = [subscription];
485
- }
486
- results = [];
487
- for (i = 0, len = subscriptions.length; i < len; i++) {
488
- subscription = subscriptions[i];
489
- results.push(typeof subscription[callbackName] === "function" ? subscription[callbackName].apply(subscription, args) : void 0);
490
- }
491
- return results;
492
- };
493
-
494
- Subscriptions.prototype.sendCommand = function(subscription, command) {
495
- var identifier;
496
- identifier = subscription.identifier;
497
- return this.consumer.send({
498
- command: command,
499
- identifier: identifier
500
- });
501
- };
502
-
503
- return Subscriptions;
504
-
505
- })();
506
-
507
- }).call(this);
508
- (function() {
509
- ActionCable.Subscription = (function() {
510
- var extend;
511
-
512
- function Subscription(consumer, params, mixin) {
513
- this.consumer = consumer;
514
- if (params == null) {
515
- params = {};
516
- }
517
- this.identifier = JSON.stringify(params);
518
- extend(this, mixin);
519
- }
520
-
521
- Subscription.prototype.perform = function(action, data) {
522
- if (data == null) {
523
- data = {};
524
- }
525
- data.action = action;
526
- return this.send(data);
527
- };
528
-
529
- Subscription.prototype.send = function(data) {
530
- return this.consumer.send({
531
- command: "message",
532
- identifier: this.identifier,
533
- data: JSON.stringify(data)
534
- });
535
- };
536
-
537
- Subscription.prototype.unsubscribe = function() {
538
- return this.consumer.subscriptions.remove(this);
539
- };
540
-
541
- extend = function(object, properties) {
542
- var key, value;
543
- if (properties != null) {
544
- for (key in properties) {
545
- value = properties[key];
546
- object[key] = value;
547
- }
548
- }
549
- return object;
550
- };
551
-
552
- return Subscription;
553
-
554
- })();
555
-
556
- }).call(this);
557
- (function() {
558
- ActionCable.Consumer = (function() {
559
- function Consumer(url) {
560
- this.url = url;
561
- this.subscriptions = new ActionCable.Subscriptions(this);
562
- this.connection = new ActionCable.Connection(this);
563
- }
564
-
565
- Consumer.prototype.send = function(data) {
566
- return this.connection.send(data);
567
- };
568
-
569
- Consumer.prototype.connect = function() {
570
- return this.connection.open();
571
- };
572
-
573
- Consumer.prototype.disconnect = function() {
574
- return this.connection.close({
575
- allowReconnect: false
576
- });
577
- };
578
-
579
- Consumer.prototype.ensureActiveConnection = function() {
580
- if (!this.connection.isActive()) {
581
- return this.connection.open();
582
- }
583
- };
584
-
585
- return Consumer;
586
-
587
- })();
588
-
589
- }).call(this);
590
- }).call(this);
591
-
592
- if (typeof module === "object" && module.exports) {
593
- module.exports = ActionCable;
594
- } else if (typeof define === "function" && define.amd) {
595
- define(ActionCable);
596
- }
597
- }).call(this);