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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -3
- data/MIT-LICENSE +1 -1
- data/README.md +60 -44
- data/lib/action_cable.rb +2 -1
- data/lib/action_cable/channel/base.rb +2 -2
- data/lib/action_cable/channel/periodic_timers.rb +3 -3
- data/lib/action_cable/channel/streams.rb +4 -4
- data/lib/action_cable/connection.rb +4 -1
- data/lib/action_cable/connection/base.rb +22 -21
- data/lib/action_cable/connection/client_socket.rb +150 -0
- data/lib/action_cable/connection/identification.rb +1 -1
- data/lib/action_cable/connection/internal_channel.rb +6 -6
- data/lib/action_cable/connection/stream.rb +59 -0
- data/lib/action_cable/connection/stream_event_loop.rb +94 -0
- data/lib/action_cable/connection/subscriptions.rb +0 -1
- data/lib/action_cable/connection/web_socket.rb +14 -8
- data/lib/action_cable/engine.rb +3 -3
- data/lib/action_cable/gem_version.rb +1 -1
- data/lib/action_cable/remote_connections.rb +1 -1
- data/lib/action_cable/server.rb +0 -4
- data/lib/action_cable/server/base.rb +19 -22
- data/lib/action_cable/server/broadcasting.rb +1 -8
- data/lib/action_cable/server/configuration.rb +25 -5
- data/lib/action_cable/server/connections.rb +3 -5
- data/lib/action_cable/server/worker.rb +42 -13
- data/lib/action_cable/subscription_adapter.rb +8 -0
- data/lib/action_cable/subscription_adapter/async.rb +22 -0
- data/lib/action_cable/subscription_adapter/base.rb +28 -0
- data/lib/action_cable/subscription_adapter/evented_redis.rb +67 -0
- data/lib/action_cable/subscription_adapter/inline.rb +35 -0
- data/lib/action_cable/subscription_adapter/postgresql.rb +106 -0
- data/lib/action_cable/subscription_adapter/redis.rb +163 -0
- data/lib/action_cable/subscription_adapter/subscriber_map.rb +53 -0
- data/lib/assets/compiled/action_cable.js +473 -0
- data/lib/rails/generators/channel/channel_generator.rb +6 -1
- metadata +21 -99
- data/lib/action_cable/process/logging.rb +0 -10
- data/lib/assets/javascripts/action_cable.coffee.erb +0 -23
- data/lib/assets/javascripts/action_cable/connection.coffee +0 -84
- data/lib/assets/javascripts/action_cable/connection_monitor.coffee +0 -84
- data/lib/assets/javascripts/action_cable/consumer.coffee +0 -31
- data/lib/assets/javascripts/action_cable/subscription.coffee +0 -68
- 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);
|