actioncable 5.0.0.beta1.1 → 5.0.0.beta2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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);