actioncable 5.0.0.beta1.1 → 5.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -3
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +60 -44
  5. data/lib/action_cable.rb +2 -1
  6. data/lib/action_cable/channel/base.rb +2 -2
  7. data/lib/action_cable/channel/periodic_timers.rb +3 -3
  8. data/lib/action_cable/channel/streams.rb +4 -4
  9. data/lib/action_cable/connection.rb +4 -1
  10. data/lib/action_cable/connection/base.rb +22 -21
  11. data/lib/action_cable/connection/client_socket.rb +150 -0
  12. data/lib/action_cable/connection/identification.rb +1 -1
  13. data/lib/action_cable/connection/internal_channel.rb +6 -6
  14. data/lib/action_cable/connection/stream.rb +59 -0
  15. data/lib/action_cable/connection/stream_event_loop.rb +94 -0
  16. data/lib/action_cable/connection/subscriptions.rb +0 -1
  17. data/lib/action_cable/connection/web_socket.rb +14 -8
  18. data/lib/action_cable/engine.rb +3 -3
  19. data/lib/action_cable/gem_version.rb +1 -1
  20. data/lib/action_cable/remote_connections.rb +1 -1
  21. data/lib/action_cable/server.rb +0 -4
  22. data/lib/action_cable/server/base.rb +19 -22
  23. data/lib/action_cable/server/broadcasting.rb +1 -8
  24. data/lib/action_cable/server/configuration.rb +25 -5
  25. data/lib/action_cable/server/connections.rb +3 -5
  26. data/lib/action_cable/server/worker.rb +42 -13
  27. data/lib/action_cable/subscription_adapter.rb +8 -0
  28. data/lib/action_cable/subscription_adapter/async.rb +22 -0
  29. data/lib/action_cable/subscription_adapter/base.rb +28 -0
  30. data/lib/action_cable/subscription_adapter/evented_redis.rb +67 -0
  31. data/lib/action_cable/subscription_adapter/inline.rb +35 -0
  32. data/lib/action_cable/subscription_adapter/postgresql.rb +106 -0
  33. data/lib/action_cable/subscription_adapter/redis.rb +163 -0
  34. data/lib/action_cable/subscription_adapter/subscriber_map.rb +53 -0
  35. data/lib/assets/compiled/action_cable.js +473 -0
  36. data/lib/rails/generators/channel/channel_generator.rb +6 -1
  37. metadata +21 -99
  38. data/lib/action_cable/process/logging.rb +0 -10
  39. data/lib/assets/javascripts/action_cable.coffee.erb +0 -23
  40. data/lib/assets/javascripts/action_cable/connection.coffee +0 -84
  41. data/lib/assets/javascripts/action_cable/connection_monitor.coffee +0 -84
  42. data/lib/assets/javascripts/action_cable/consumer.coffee +0 -31
  43. data/lib/assets/javascripts/action_cable/subscription.coffee +0 -68
  44. data/lib/assets/javascripts/action_cable/subscriptions.coffee +0 -78
@@ -0,0 +1,163 @@
1
+ require 'thread'
2
+
3
+ gem 'redis', '~> 3.0'
4
+ require 'redis'
5
+
6
+ module ActionCable
7
+ module SubscriptionAdapter
8
+ class Redis < Base # :nodoc:
9
+ def initialize(*)
10
+ super
11
+ @listener = nil
12
+ @redis_connection_for_broadcasts = nil
13
+ end
14
+
15
+ def broadcast(channel, payload)
16
+ redis_connection_for_broadcasts.publish(channel, payload)
17
+ end
18
+
19
+ def subscribe(channel, callback, success_callback = nil)
20
+ listener.add_subscriber(channel, callback, success_callback)
21
+ end
22
+
23
+ def unsubscribe(channel, callback)
24
+ listener.remove_subscriber(channel, callback)
25
+ end
26
+
27
+ def shutdown
28
+ @listener.shutdown if @listener
29
+ end
30
+
31
+ def redis_connection_for_subscriptions
32
+ ::Redis.new(@server.config.cable)
33
+ end
34
+
35
+ private
36
+ def listener
37
+ @listener || @server.mutex.synchronize { @listener ||= Listener.new(self) }
38
+ end
39
+
40
+ def redis_connection_for_broadcasts
41
+ @redis_connection_for_broadcasts || @server.mutex.synchronize do
42
+ @redis_connection_for_broadcasts ||= ::Redis.new(@server.config.cable)
43
+ end
44
+ end
45
+
46
+ class Listener < SubscriberMap
47
+ def initialize(adapter)
48
+ super()
49
+
50
+ @adapter = adapter
51
+
52
+ @subscribe_callbacks = Hash.new { |h, k| h[k] = [] }
53
+ @subscription_lock = Mutex.new
54
+
55
+ @raw_client = nil
56
+
57
+ @when_connected = []
58
+
59
+ @thread = nil
60
+ end
61
+
62
+ def listen(conn)
63
+ conn.without_reconnect do
64
+ original_client = conn.client
65
+
66
+ conn.subscribe('_action_cable_internal') do |on|
67
+ on.subscribe do |chan, count|
68
+ @subscription_lock.synchronize do
69
+ if count == 1
70
+ @raw_client = original_client
71
+
72
+ until @when_connected.empty?
73
+ @when_connected.shift.call
74
+ end
75
+ end
76
+
77
+ if callbacks = @subscribe_callbacks[chan]
78
+ next_callback = callbacks.shift
79
+ Concurrent.global_io_executor << next_callback if next_callback
80
+ @subscribe_callbacks.delete(chan) if callbacks.empty?
81
+ end
82
+ end
83
+ end
84
+
85
+ on.message do |chan, message|
86
+ broadcast(chan, message)
87
+ end
88
+
89
+ on.unsubscribe do |chan, count|
90
+ if count == 0
91
+ @subscription_lock.synchronize do
92
+ @raw_client = nil
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ def shutdown
101
+ @subscription_lock.synchronize do
102
+ return if @thread.nil?
103
+
104
+ when_connected do
105
+ send_command('unsubscribe')
106
+ @raw_client = nil
107
+ end
108
+ end
109
+
110
+ Thread.pass while @thread.alive?
111
+ end
112
+
113
+ def add_channel(channel, on_success)
114
+ @subscription_lock.synchronize do
115
+ ensure_listener_running
116
+ @subscribe_callbacks[channel] << on_success
117
+ when_connected { send_command('subscribe', channel) }
118
+ end
119
+ end
120
+
121
+ def remove_channel(channel)
122
+ @subscription_lock.synchronize do
123
+ when_connected { send_command('unsubscribe', channel) }
124
+ end
125
+ end
126
+
127
+ def invoke_callback(*)
128
+ Concurrent.global_io_executor.post { super }
129
+ end
130
+
131
+ private
132
+ def ensure_listener_running
133
+ @thread ||= Thread.new do
134
+ Thread.current.abort_on_exception = true
135
+
136
+ conn = @adapter.redis_connection_for_subscriptions
137
+ listen conn
138
+ end
139
+ end
140
+
141
+ def when_connected(&block)
142
+ if @raw_client
143
+ block.call
144
+ else
145
+ @when_connected << block
146
+ end
147
+ end
148
+
149
+ def send_command(*command)
150
+ @raw_client.write(command)
151
+
152
+ very_raw_connection =
153
+ @raw_client.connection.instance_variable_defined?(:@connection) &&
154
+ @raw_client.connection.instance_variable_get(:@connection)
155
+
156
+ if very_raw_connection && very_raw_connection.respond_to?(:flush)
157
+ very_raw_connection.flush
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,53 @@
1
+ module ActionCable
2
+ module SubscriptionAdapter
3
+ class SubscriberMap
4
+ def initialize
5
+ @subscribers = Hash.new { |h,k| h[k] = [] }
6
+ @sync = Mutex.new
7
+ end
8
+
9
+ def add_subscriber(channel, subscriber, on_success)
10
+ @sync.synchronize do
11
+ new_channel = !@subscribers.key?(channel)
12
+
13
+ @subscribers[channel] << subscriber
14
+
15
+ if new_channel
16
+ add_channel channel, on_success
17
+ elsif on_success
18
+ on_success.call
19
+ end
20
+ end
21
+ end
22
+
23
+ def remove_subscriber(channel, subscriber)
24
+ @sync.synchronize do
25
+ @subscribers[channel].delete(subscriber)
26
+
27
+ if @subscribers[channel].empty?
28
+ @subscribers.delete channel
29
+ remove_channel channel
30
+ end
31
+ end
32
+ end
33
+
34
+ def broadcast(channel, message)
35
+ list = @sync.synchronize { @subscribers[channel].dup }
36
+ list.each do |subscriber|
37
+ invoke_callback(subscriber, message)
38
+ end
39
+ end
40
+
41
+ def add_channel(channel, on_success)
42
+ on_success.call if on_success
43
+ end
44
+
45
+ def remove_channel(channel)
46
+ end
47
+
48
+ def invoke_callback(callback, message)
49
+ callback.call message
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,473 @@
1
+ (function() {
2
+ this.ActionCable = {
3
+ INTERNAL: {
4
+ "identifiers": {
5
+ "ping": "_ping"
6
+ },
7
+ "message_types": {
8
+ "confirmation": "confirm_subscription",
9
+ "rejection": "reject_subscription"
10
+ }
11
+ },
12
+ createConsumer: function(url) {
13
+ if (url == null) {
14
+ url = this.getConfig("url");
15
+ }
16
+ return new ActionCable.Consumer(this.createWebSocketURL(url));
17
+ },
18
+ getConfig: function(name) {
19
+ var element;
20
+ element = document.head.querySelector("meta[name='action-cable-" + name + "']");
21
+ return element != null ? element.getAttribute("content") : void 0;
22
+ },
23
+ createWebSocketURL: function(url) {
24
+ var a;
25
+ if (url && !/^wss?:/i.test(url)) {
26
+ a = document.createElement("a");
27
+ a.href = url;
28
+ a.href = a.href;
29
+ a.protocol = a.protocol.replace("http", "ws");
30
+ return a.href;
31
+ } else {
32
+ return url;
33
+ }
34
+ }
35
+ };
36
+
37
+ }).call(this);
38
+ (function() {
39
+ var message_types,
40
+ bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
41
+ slice = [].slice,
42
+ 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; };
43
+
44
+ message_types = ActionCable.INTERNAL.message_types;
45
+
46
+ ActionCable.Connection = (function() {
47
+ Connection.reopenDelay = 500;
48
+
49
+ function Connection(consumer) {
50
+ this.consumer = consumer;
51
+ this.open = bind(this.open, this);
52
+ this.open();
53
+ }
54
+
55
+ Connection.prototype.send = function(data) {
56
+ if (this.isOpen()) {
57
+ this.webSocket.send(JSON.stringify(data));
58
+ return true;
59
+ } else {
60
+ return false;
61
+ }
62
+ };
63
+
64
+ Connection.prototype.open = function() {
65
+ if (this.webSocket && !this.isState("closed")) {
66
+ throw new Error("Existing connection must be closed before opening");
67
+ } else {
68
+ this.webSocket = new WebSocket(this.consumer.url);
69
+ this.installEventHandlers();
70
+ return true;
71
+ }
72
+ };
73
+
74
+ Connection.prototype.close = function() {
75
+ var ref;
76
+ return (ref = this.webSocket) != null ? ref.close() : void 0;
77
+ };
78
+
79
+ Connection.prototype.reopen = function() {
80
+ if (this.isState("closed")) {
81
+ return this.open();
82
+ } else {
83
+ try {
84
+ return this.close();
85
+ } finally {
86
+ setTimeout(this.open, this.constructor.reopenDelay);
87
+ }
88
+ }
89
+ };
90
+
91
+ Connection.prototype.isOpen = function() {
92
+ return this.isState("open");
93
+ };
94
+
95
+ Connection.prototype.isState = function() {
96
+ var ref, states;
97
+ states = 1 <= arguments.length ? slice.call(arguments, 0) : [];
98
+ return ref = this.getState(), indexOf.call(states, ref) >= 0;
99
+ };
100
+
101
+ Connection.prototype.getState = function() {
102
+ var ref, state, value;
103
+ for (state in WebSocket) {
104
+ value = WebSocket[state];
105
+ if (value === ((ref = this.webSocket) != null ? ref.readyState : void 0)) {
106
+ return state.toLowerCase();
107
+ }
108
+ }
109
+ return null;
110
+ };
111
+
112
+ Connection.prototype.installEventHandlers = function() {
113
+ var eventName, handler;
114
+ for (eventName in this.events) {
115
+ handler = this.events[eventName].bind(this);
116
+ this.webSocket["on" + eventName] = handler;
117
+ }
118
+ };
119
+
120
+ Connection.prototype.events = {
121
+ message: function(event) {
122
+ var identifier, message, ref, type;
123
+ ref = JSON.parse(event.data), identifier = ref.identifier, message = ref.message, type = ref.type;
124
+ switch (type) {
125
+ case message_types.confirmation:
126
+ return this.consumer.subscriptions.notify(identifier, "connected");
127
+ case message_types.rejection:
128
+ return this.consumer.subscriptions.reject(identifier);
129
+ default:
130
+ return this.consumer.subscriptions.notify(identifier, "received", message);
131
+ }
132
+ },
133
+ open: function() {
134
+ this.disconnected = false;
135
+ return this.consumer.subscriptions.reload();
136
+ },
137
+ close: function() {
138
+ return this.disconnect();
139
+ },
140
+ error: function() {
141
+ return this.disconnect();
142
+ }
143
+ };
144
+
145
+ Connection.prototype.disconnect = function() {
146
+ if (this.disconnected) {
147
+ return;
148
+ }
149
+ this.disconnected = true;
150
+ return this.consumer.subscriptions.notifyAll("disconnected");
151
+ };
152
+
153
+ return Connection;
154
+
155
+ })();
156
+
157
+ }).call(this);
158
+ (function() {
159
+ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
160
+
161
+ ActionCable.ConnectionMonitor = (function() {
162
+ var clamp, now, secondsSince;
163
+
164
+ ConnectionMonitor.pollInterval = {
165
+ min: 3,
166
+ max: 30
167
+ };
168
+
169
+ ConnectionMonitor.staleThreshold = 6;
170
+
171
+ ConnectionMonitor.prototype.identifier = ActionCable.INTERNAL.identifiers.ping;
172
+
173
+ function ConnectionMonitor(consumer) {
174
+ this.consumer = consumer;
175
+ this.visibilityDidChange = bind(this.visibilityDidChange, this);
176
+ this.consumer.subscriptions.add(this);
177
+ this.start();
178
+ }
179
+
180
+ ConnectionMonitor.prototype.connected = function() {
181
+ this.reset();
182
+ this.pingedAt = now();
183
+ return delete this.disconnectedAt;
184
+ };
185
+
186
+ ConnectionMonitor.prototype.disconnected = function() {
187
+ return this.disconnectedAt = now();
188
+ };
189
+
190
+ ConnectionMonitor.prototype.received = function() {
191
+ return this.pingedAt = now();
192
+ };
193
+
194
+ ConnectionMonitor.prototype.reset = function() {
195
+ return this.reconnectAttempts = 0;
196
+ };
197
+
198
+ ConnectionMonitor.prototype.start = function() {
199
+ this.reset();
200
+ delete this.stoppedAt;
201
+ this.startedAt = now();
202
+ this.poll();
203
+ return document.addEventListener("visibilitychange", this.visibilityDidChange);
204
+ };
205
+
206
+ ConnectionMonitor.prototype.stop = function() {
207
+ this.stoppedAt = now();
208
+ return document.removeEventListener("visibilitychange", this.visibilityDidChange);
209
+ };
210
+
211
+ ConnectionMonitor.prototype.poll = function() {
212
+ return setTimeout((function(_this) {
213
+ return function() {
214
+ if (!_this.stoppedAt) {
215
+ _this.reconnectIfStale();
216
+ return _this.poll();
217
+ }
218
+ };
219
+ })(this), this.getInterval());
220
+ };
221
+
222
+ ConnectionMonitor.prototype.getInterval = function() {
223
+ var interval, max, min, ref;
224
+ ref = this.constructor.pollInterval, min = ref.min, max = ref.max;
225
+ interval = 5 * Math.log(this.reconnectAttempts + 1);
226
+ return clamp(interval, min, max) * 1000;
227
+ };
228
+
229
+ ConnectionMonitor.prototype.reconnectIfStale = function() {
230
+ if (this.connectionIsStale()) {
231
+ this.reconnectAttempts++;
232
+ if (!this.disconnectedRecently()) {
233
+ return this.consumer.connection.reopen();
234
+ }
235
+ }
236
+ };
237
+
238
+ ConnectionMonitor.prototype.connectionIsStale = function() {
239
+ var ref;
240
+ return secondsSince((ref = this.pingedAt) != null ? ref : this.startedAt) > this.constructor.staleThreshold;
241
+ };
242
+
243
+ ConnectionMonitor.prototype.disconnectedRecently = function() {
244
+ return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold;
245
+ };
246
+
247
+ ConnectionMonitor.prototype.visibilityDidChange = function() {
248
+ if (document.visibilityState === "visible") {
249
+ return setTimeout((function(_this) {
250
+ return function() {
251
+ if (_this.connectionIsStale() || !_this.consumer.connection.isOpen()) {
252
+ return _this.consumer.connection.reopen();
253
+ }
254
+ };
255
+ })(this), 200);
256
+ }
257
+ };
258
+
259
+ now = function() {
260
+ return new Date().getTime();
261
+ };
262
+
263
+ secondsSince = function(time) {
264
+ return (now() - time) / 1000;
265
+ };
266
+
267
+ clamp = function(number, min, max) {
268
+ return Math.max(min, Math.min(max, number));
269
+ };
270
+
271
+ return ConnectionMonitor;
272
+
273
+ })();
274
+
275
+ }).call(this);
276
+ (function() {
277
+ var slice = [].slice;
278
+
279
+ ActionCable.Subscriptions = (function() {
280
+ function Subscriptions(consumer) {
281
+ this.consumer = consumer;
282
+ this.subscriptions = [];
283
+ }
284
+
285
+ Subscriptions.prototype.create = function(channelName, mixin) {
286
+ var channel, params;
287
+ channel = channelName;
288
+ params = typeof channel === "object" ? channel : {
289
+ channel: channel
290
+ };
291
+ return new ActionCable.Subscription(this, params, mixin);
292
+ };
293
+
294
+ Subscriptions.prototype.add = function(subscription) {
295
+ this.subscriptions.push(subscription);
296
+ this.notify(subscription, "initialized");
297
+ return this.sendCommand(subscription, "subscribe");
298
+ };
299
+
300
+ Subscriptions.prototype.remove = function(subscription) {
301
+ this.forget(subscription);
302
+ if (!this.findAll(subscription.identifier).length) {
303
+ return this.sendCommand(subscription, "unsubscribe");
304
+ }
305
+ };
306
+
307
+ Subscriptions.prototype.reject = function(identifier) {
308
+ var i, len, ref, results, subscription;
309
+ ref = this.findAll(identifier);
310
+ results = [];
311
+ for (i = 0, len = ref.length; i < len; i++) {
312
+ subscription = ref[i];
313
+ this.forget(subscription);
314
+ results.push(this.notify(subscription, "rejected"));
315
+ }
316
+ return results;
317
+ };
318
+
319
+ Subscriptions.prototype.forget = function(subscription) {
320
+ var s;
321
+ return this.subscriptions = (function() {
322
+ var i, len, ref, results;
323
+ ref = this.subscriptions;
324
+ results = [];
325
+ for (i = 0, len = ref.length; i < len; i++) {
326
+ s = ref[i];
327
+ if (s !== subscription) {
328
+ results.push(s);
329
+ }
330
+ }
331
+ return results;
332
+ }).call(this);
333
+ };
334
+
335
+ Subscriptions.prototype.findAll = function(identifier) {
336
+ var i, len, ref, results, s;
337
+ ref = this.subscriptions;
338
+ results = [];
339
+ for (i = 0, len = ref.length; i < len; i++) {
340
+ s = ref[i];
341
+ if (s.identifier === identifier) {
342
+ results.push(s);
343
+ }
344
+ }
345
+ return results;
346
+ };
347
+
348
+ Subscriptions.prototype.reload = function() {
349
+ var i, len, ref, results, subscription;
350
+ ref = this.subscriptions;
351
+ results = [];
352
+ for (i = 0, len = ref.length; i < len; i++) {
353
+ subscription = ref[i];
354
+ results.push(this.sendCommand(subscription, "subscribe"));
355
+ }
356
+ return results;
357
+ };
358
+
359
+ Subscriptions.prototype.notifyAll = function() {
360
+ var args, callbackName, i, len, ref, results, subscription;
361
+ callbackName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
362
+ ref = this.subscriptions;
363
+ results = [];
364
+ for (i = 0, len = ref.length; i < len; i++) {
365
+ subscription = ref[i];
366
+ results.push(this.notify.apply(this, [subscription, callbackName].concat(slice.call(args))));
367
+ }
368
+ return results;
369
+ };
370
+
371
+ Subscriptions.prototype.notify = function() {
372
+ var args, callbackName, i, len, results, subscription, subscriptions;
373
+ subscription = arguments[0], callbackName = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
374
+ if (typeof subscription === "string") {
375
+ subscriptions = this.findAll(subscription);
376
+ } else {
377
+ subscriptions = [subscription];
378
+ }
379
+ results = [];
380
+ for (i = 0, len = subscriptions.length; i < len; i++) {
381
+ subscription = subscriptions[i];
382
+ results.push(typeof subscription[callbackName] === "function" ? subscription[callbackName].apply(subscription, args) : void 0);
383
+ }
384
+ return results;
385
+ };
386
+
387
+ Subscriptions.prototype.sendCommand = function(subscription, command) {
388
+ var identifier;
389
+ identifier = subscription.identifier;
390
+ if (identifier === ActionCable.INTERNAL.identifiers.ping) {
391
+ return this.consumer.connection.isOpen();
392
+ } else {
393
+ return this.consumer.send({
394
+ command: command,
395
+ identifier: identifier
396
+ });
397
+ }
398
+ };
399
+
400
+ return Subscriptions;
401
+
402
+ })();
403
+
404
+ }).call(this);
405
+ (function() {
406
+ ActionCable.Subscription = (function() {
407
+ var extend;
408
+
409
+ function Subscription(subscriptions, params, mixin) {
410
+ this.subscriptions = subscriptions;
411
+ if (params == null) {
412
+ params = {};
413
+ }
414
+ this.identifier = JSON.stringify(params);
415
+ extend(this, mixin);
416
+ this.subscriptions.add(this);
417
+ this.consumer = this.subscriptions.consumer;
418
+ }
419
+
420
+ Subscription.prototype.perform = function(action, data) {
421
+ if (data == null) {
422
+ data = {};
423
+ }
424
+ data.action = action;
425
+ return this.send(data);
426
+ };
427
+
428
+ Subscription.prototype.send = function(data) {
429
+ return this.consumer.send({
430
+ command: "message",
431
+ identifier: this.identifier,
432
+ data: JSON.stringify(data)
433
+ });
434
+ };
435
+
436
+ Subscription.prototype.unsubscribe = function() {
437
+ return this.subscriptions.remove(this);
438
+ };
439
+
440
+ extend = function(object, properties) {
441
+ var key, value;
442
+ if (properties != null) {
443
+ for (key in properties) {
444
+ value = properties[key];
445
+ object[key] = value;
446
+ }
447
+ }
448
+ return object;
449
+ };
450
+
451
+ return Subscription;
452
+
453
+ })();
454
+
455
+ }).call(this);
456
+ (function() {
457
+ ActionCable.Consumer = (function() {
458
+ function Consumer(url) {
459
+ this.url = url;
460
+ this.subscriptions = new ActionCable.Subscriptions(this);
461
+ this.connection = new ActionCable.Connection(this);
462
+ this.connectionMonitor = new ActionCable.ConnectionMonitor(this);
463
+ }
464
+
465
+ Consumer.prototype.send = function(data) {
466
+ return this.connection.send(data);
467
+ };
468
+
469
+ return Consumer;
470
+
471
+ })();
472
+
473
+ }).call(this);