plezi 0.14.4 → 0.14.5
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 +12 -0
- data/bin/ws_shootout +30 -0
- data/exe/plezi +108 -108
- data/lib/plezi.rb +18 -26
- data/lib/plezi/activation.rb +16 -16
- data/lib/plezi/api.rb +51 -50
- data/lib/plezi/controller/controller.rb +249 -229
- data/lib/plezi/controller/controller_class.rb +189 -174
- data/lib/plezi/controller/cookies.rb +49 -47
- data/lib/plezi/helpers.rb +35 -35
- data/lib/plezi/render/erb.rb +25 -26
- data/lib/plezi/render/has_cache.rb +31 -31
- data/lib/plezi/render/markdown.rb +53 -53
- data/lib/plezi/render/render.rb +36 -38
- data/lib/plezi/render/sass.rb +43 -44
- data/lib/plezi/render/slim.rb +25 -25
- data/lib/plezi/router/adclient.rb +14 -15
- data/lib/plezi/router/assets.rb +59 -61
- data/lib/plezi/router/errors.rb +22 -22
- data/lib/plezi/router/route.rb +98 -100
- data/lib/plezi/router/router.rb +120 -113
- data/lib/plezi/version.rb +1 -1
- data/lib/plezi/websockets/message_dispatch.rb +118 -80
- data/lib/plezi/websockets/redis.rb +42 -43
- data/plezi.gemspec +3 -3
- data/resources/client.js +229 -204
- data/resources/ctrlr.rb +26 -26
- data/resources/mini_app.rb +1 -1
- data/resources/simple-client.js +50 -43
- metadata +9 -8
@@ -1,55 +1,54 @@
|
|
1
|
-
require 'securerandom'
|
2
1
|
module Plezi
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
module Base
|
3
|
+
module MessageDispatch
|
4
|
+
module RedisDriver
|
5
|
+
@redis_locker ||= Mutex.new
|
6
|
+
@redis = @redis_sub_thread = nil
|
8
7
|
|
9
|
-
|
8
|
+
module_function
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
10
|
+
def connect
|
11
|
+
return false unless ENV['PL_REDIS_URL'] && defined?(::Redis)
|
12
|
+
return @redis if (@redis_sub_thread && @redis_sub_thread.alive?) && @redis
|
13
|
+
@redis_locker.synchronize do
|
14
|
+
return @redis if (@redis_sub_thread && @redis_sub_thread.alive?) && @redis # repeat the test inside syncing, things change.
|
15
|
+
@redis.quit if @redis
|
16
|
+
@redis = ::Redis.new(url: ENV['PL_REDIS_URL'])
|
17
|
+
raise "Redis connction failed for: #{ENV['PL_REDIS_URL']}" unless @redis
|
18
|
+
@redis_sub_thread = Thread.new do
|
19
|
+
begin
|
20
|
+
::Redis.new(url: ENV['PL_REDIS_URL']).subscribe(::Plezi.app_name, ::Plezi::Base::MessageDispatch.pid) do |on|
|
21
|
+
on.message do |_channel, msg|
|
22
|
+
::Plezi::Base::MessageDispatch << msg
|
23
|
+
end
|
24
|
+
end
|
25
|
+
rescue => e
|
26
|
+
puts e.message, e.backtrace
|
27
|
+
retry
|
28
|
+
end
|
24
29
|
end
|
25
|
-
|
26
|
-
|
27
|
-
puts e.message, e.backtrace
|
28
|
-
retry
|
29
|
-
end
|
30
|
+
@redis
|
31
|
+
end
|
30
32
|
end
|
31
|
-
@redis
|
32
|
-
end
|
33
|
-
end
|
34
33
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
34
|
+
# Get the current redis connection.
|
35
|
+
def redis
|
36
|
+
@redis || connect
|
37
|
+
end
|
39
38
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
39
|
+
def push(channel, message)
|
40
|
+
return unless connect
|
41
|
+
return if away?(channel)
|
42
|
+
redis.publish(channel, message)
|
43
|
+
end
|
45
44
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
45
|
+
def away?(server)
|
46
|
+
return true unless connect
|
47
|
+
@redis.pubsub('CHANNELS', server).empty?
|
48
|
+
end
|
49
|
+
end
|
50
50
|
end
|
51
|
-
|
52
|
-
end
|
51
|
+
end
|
53
52
|
end
|
54
53
|
|
55
54
|
::Plezi::Base::MessageDispatch.drivers << ::Plezi::Base::MessageDispatch::RedisDriver
|
data/plezi.gemspec
CHANGED
@@ -27,12 +27,12 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
28
|
spec.require_paths = ['lib']
|
29
29
|
|
30
|
-
spec.add_dependency 'iodine', '~> 0.2', '>= 0.2.
|
30
|
+
spec.add_dependency 'iodine', '~> 0.2', '>= 0.2.14'
|
31
31
|
spec.add_dependency 'rack', '>= 2.0.0'
|
32
|
-
spec.add_dependency 'bundler', '~> 1.
|
32
|
+
spec.add_dependency 'bundler', '~> 1.14'
|
33
33
|
# spec.add_dependency 'redcarpet', '> 3.3.0'
|
34
34
|
# spec.add_dependency 'slim', '> 3.0.0'
|
35
35
|
|
36
|
-
spec.add_development_dependency 'rake', '~>
|
36
|
+
spec.add_development_dependency 'rake', '~> 11.0'
|
37
37
|
spec.add_development_dependency 'minitest', '~> 5.0'
|
38
38
|
end
|
data/resources/client.js
CHANGED
@@ -7,16 +7,20 @@
|
|
7
7
|
//
|
8
8
|
// var client = new PleziClient()
|
9
9
|
//
|
10
|
-
// To open a connection to a different path for the original server (SSL will be
|
10
|
+
// To open a connection to a different path for the original server (SSL will be
|
11
|
+
// preserved when in use), use:
|
11
12
|
//
|
12
13
|
// var client = new PleziClient(PleziClient.origin + "/path")
|
13
14
|
//
|
14
|
-
// To automatically renew the connection when disconnections are reported by the
|
15
|
+
// To automatically renew the connection when disconnections are reported by the
|
16
|
+
// browser, use:
|
15
17
|
//
|
16
18
|
// client.autoreconnect = true
|
17
|
-
// client.reconnect_interval = 250 // sets how long to wait before
|
19
|
+
// client.reconnect_interval = 250 // sets how long to wait before
|
20
|
+
// reconnection attempts. default is 250 ms.
|
18
21
|
//
|
19
|
-
// To set up event handling, directly set an `<event name>` callback. i.e., for
|
22
|
+
// To set up event handling, directly set an `<event name>` callback. i.e., for
|
23
|
+
// an event called `chat`:
|
20
24
|
//
|
21
25
|
// client.chat = function(event) { "..." }
|
22
26
|
//
|
@@ -25,229 +29,250 @@
|
|
25
29
|
// client.emit({event: "chat", data: "the message"})
|
26
30
|
//
|
27
31
|
function PleziClient(url, autoreconnect) {
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
32
|
+
// Set URL
|
33
|
+
if (url) {
|
34
|
+
this.url = url;
|
35
|
+
} else {
|
36
|
+
this.url = PleziClient.origin + self.location.pathname;
|
37
|
+
}
|
38
|
+
// Connect Websocket
|
39
|
+
this.reconnect();
|
40
|
+
// Setup AJAJ
|
41
|
+
this.ajaj = {};
|
42
|
+
this.ajaj.client = this;
|
43
|
+
this.ajaj.url = this.url.replace(/^ws:\/\//i, "http://")
|
44
|
+
.replace(/^wss:\/\//i, "https://");
|
45
|
+
this.ajaj.add = {};
|
46
|
+
this.ajaj.emit = this.___ajaj__emit;
|
47
|
+
this.ajaj.auto = false;
|
48
|
+
// auto-reconnection
|
49
|
+
this.autoreconnect = false;
|
50
|
+
this.reconnect_interval = 200;
|
51
|
+
// dump data to console?
|
52
|
+
this.log_events = false;
|
53
|
+
// the timeout for a message ack receipt
|
54
|
+
this.emit_timeout = false;
|
55
|
+
// Set the autoreconnect property
|
56
|
+
if (autoreconnect) {
|
57
|
+
this.autoreconnect = true;
|
58
|
+
}
|
54
59
|
}
|
55
60
|
// The Websocket onopen callback
|
56
|
-
PleziClient.prototype.___on_open =
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
61
|
+
PleziClient.prototype.___on_open =
|
62
|
+
function(e) {
|
63
|
+
this.owner.connected = true;
|
64
|
+
if (this.owner.onopen) {
|
65
|
+
this.owner.onopen(e);
|
66
|
+
}
|
67
|
+
}
|
62
68
|
// The Websocket onclose callback
|
63
|
-
PleziClient.prototype.___on_close =
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
PleziClient.prototype.___on_error =
|
76
|
-
|
77
|
-
|
78
|
-
|
69
|
+
PleziClient.prototype.___on_close =
|
70
|
+
function(e) {
|
71
|
+
this.owner.connected = false;
|
72
|
+
if (this.owner.onclose) {
|
73
|
+
this.owner.onclose(e);
|
74
|
+
}
|
75
|
+
if (this.owner.autoreconnect) {
|
76
|
+
setTimeout(function(obj) { obj.reconnect(); },
|
77
|
+
this.owner.reconnect_interval, this.owner);
|
78
|
+
}
|
79
|
+
}
|
80
|
+
// The Websocket onerror callback
|
81
|
+
PleziClient.prototype.___on_error =
|
82
|
+
function(e) {
|
83
|
+
if (this.owner.onerror) {
|
84
|
+
this.owner.onerror(e);
|
85
|
+
}
|
86
|
+
}
|
87
|
+
// The Websocket onmessage callback
|
88
|
+
PleziClient.prototype.___on_message = function(e) {
|
89
|
+
try {
|
90
|
+
var msg = JSON.parse(e.data);
|
91
|
+
this.owner.___dispatch(msg);
|
92
|
+
} catch (err) {
|
93
|
+
console.error(
|
94
|
+
"PleziClient experienced an error parsing the following data (not JSON):",
|
95
|
+
err, e);
|
96
|
+
}
|
97
|
+
};
|
98
|
+
|
99
|
+
PleziClient.prototype.___dispatch =
|
100
|
+
function(msg) {
|
101
|
+
try {
|
102
|
+
if (this.log_events) {
|
103
|
+
console.log(msg);
|
79
104
|
}
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
console.error("PleziClient experienced an error parsing the following data (not JSON):",
|
87
|
-
err, e);
|
105
|
+
if (msg.event == '_ack_') {
|
106
|
+
clearTimeout(msg._EID_);
|
107
|
+
if (this[msg._EID_] && this[msg._EID_].callback) {
|
108
|
+
this[msg._EID_].callback(this[msg._EID_].event, this);
|
109
|
+
}
|
110
|
+
delete this[msg._EID_];
|
88
111
|
}
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
}
|
100
|
-
delete this[msg._EID_];
|
101
|
-
}
|
102
|
-
if ((msg.event) && (this[msg.event])) {
|
103
|
-
this[msg.event](msg);
|
104
|
-
} else if ((msg.event) && (this['on' + msg.event])) {
|
105
|
-
console.warn('PleziClient: use a callback called "' + msg.event +
|
106
|
-
'" instead of of "on' + msg.event + '"');
|
107
|
-
this['on' + msg.event](msg);
|
108
|
-
} else {
|
109
|
-
if (this['unknown'] && (msg.event != '_ack_')) {
|
110
|
-
this['unknown'](msg);
|
111
|
-
};
|
112
|
-
}
|
113
|
-
} catch (err) {
|
114
|
-
console.error("PleziClient experienced an error while responding to the following onmessage event",
|
115
|
-
err, msg);
|
112
|
+
if ((msg.event) && (this[msg.event])) {
|
113
|
+
this[msg.event](msg);
|
114
|
+
} else if ((msg.event) && (this['on' + msg.event])) {
|
115
|
+
console.warn('PleziClient: use a callback called "' + msg.event +
|
116
|
+
'" instead of of "on' + msg.event + '"');
|
117
|
+
this['on' + msg.event](msg);
|
118
|
+
} else {
|
119
|
+
if (this['unknown'] && (msg.event != '_ack_')) {
|
120
|
+
this['unknown'](msg);
|
121
|
+
};
|
116
122
|
}
|
123
|
+
} catch (err) {
|
124
|
+
console.error(
|
125
|
+
"PleziClient experienced an error while responding to the following onmessage event",
|
126
|
+
err, msg);
|
127
|
+
}
|
117
128
|
}
|
118
129
|
|
119
|
-
// Clears meta timeout data
|
120
|
-
PleziClient.prototype.___perform_timeout_callback =
|
121
|
-
|
122
|
-
|
130
|
+
// Clears meta timeout data
|
131
|
+
PleziClient.prototype.___perform_timeout_callback =
|
132
|
+
function(event, pl_client, callback) {
|
133
|
+
delete pl_client[event._EID_];
|
134
|
+
callback(event, pl_client);
|
123
135
|
}
|
124
136
|
|
125
|
-
// Sets a timeout for the websocket message
|
126
|
-
PleziClient.prototype.___set_failed_timeout =
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
}
|
137
|
+
// Sets a timeout for the websocket message
|
138
|
+
PleziClient.prototype.___set_failed_timeout =
|
139
|
+
function(event, callback, timeout) {
|
140
|
+
if (event._EID_) {
|
141
|
+
return event;
|
142
|
+
};
|
143
|
+
if (!timeout) {
|
144
|
+
timeout = this.emit_timeout;
|
145
|
+
};
|
146
|
+
if (!callback) {
|
147
|
+
callback = this.___on_timeout;
|
148
|
+
};
|
149
|
+
if (!timeout) {
|
150
|
+
return event;
|
151
|
+
};
|
152
|
+
event._EID_ = setTimeout(this.___perform_timeout_callback, timeout, event,
|
153
|
+
this, callback);
|
154
|
+
return event;
|
155
|
+
}
|
156
|
+
// Removes the _client_ property from the event and
|
157
|
+
// calls
|
158
|
+
// the ontimeout callback within the correct scope
|
159
|
+
PleziClient.prototype.___on_timeout =
|
160
|
+
function(event, client) {
|
161
|
+
if (client.ajaj.auto) {
|
162
|
+
if (client.log_events) {
|
163
|
+
console.log("falling back on AJAJ for the event:", event);
|
153
164
|
}
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
console.log(this);
|
165
|
+
client.ajaj.emit(event, client.ontimeout);
|
166
|
+
} else {
|
167
|
+
client.ontimeout(event);
|
168
|
+
}
|
159
169
|
}
|
160
|
-
|
161
|
-
PleziClient.prototype.
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
this.ws.onopen = this.___on_open;
|
168
|
-
// The Websocket onclose callback
|
169
|
-
this.ws.onclose = this.___on_close;
|
170
|
-
// The Websocket onerror callback
|
171
|
-
this.ws.onerror = this.___on_error;
|
172
|
-
// The Websocket onmessage callback
|
173
|
-
this.ws.onmessage = this.___on_message;
|
170
|
+
// The timeout callback
|
171
|
+
PleziClient.prototype.ontimeout =
|
172
|
+
function(event) {
|
173
|
+
console.warn("Timeout reached - it's assumed the connection was lost " +
|
174
|
+
"and the following event was ignored by the server:",
|
175
|
+
event);
|
176
|
+
console.log(this);
|
174
177
|
}
|
175
178
|
|
176
|
-
PleziClient.prototype.
|
177
|
-
|
178
|
-
|
179
|
+
PleziClient.prototype.reconnect =
|
180
|
+
function() {
|
181
|
+
this.connected = NaN;
|
182
|
+
this.ws = new WebSocket(this.url);
|
183
|
+
// lets us access the client from the callbacks
|
184
|
+
this.ws.owner = this;
|
185
|
+
// The Websocket onopen callback
|
186
|
+
this.ws.onopen = this.___on_open;
|
187
|
+
// The Websocket onclose callback
|
188
|
+
this.ws.onclose = this.___on_close;
|
189
|
+
// The Websocket onerror callback
|
190
|
+
this.ws.onerror = this.___on_error;
|
191
|
+
// The Websocket onmessage callback
|
192
|
+
this.ws.onmessage = this.___on_message;
|
179
193
|
}
|
180
194
|
|
181
|
-
PleziClient.
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
return false;
|
186
|
-
}
|
187
|
-
this.ws.send(data);
|
188
|
-
if (this.ws.readyState != 1) {
|
189
|
-
return false;
|
190
|
-
}
|
191
|
-
return true;
|
195
|
+
PleziClient.prototype.close =
|
196
|
+
function() {
|
197
|
+
this.autoreconnect = false;
|
198
|
+
this.ws.close();
|
192
199
|
}
|
193
200
|
|
194
|
-
PleziClient.
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
201
|
+
PleziClient.origin =
|
202
|
+
(self.location.protocol.match(/https/) ? 'wws'
|
203
|
+
: 'ws') +
|
204
|
+
'://' + self.location.hostname +
|
205
|
+
(self.location.port == ''
|
206
|
+
? ''
|
207
|
+
: (':' + self.location.port));
|
208
|
+
|
209
|
+
PleziClient.prototype.sendraw =
|
210
|
+
function(data) {
|
211
|
+
if (this.ws.readyState != 1) {
|
212
|
+
return false;
|
213
|
+
}
|
214
|
+
this.ws.send(data);
|
215
|
+
if (this.ws.readyState != 1) {
|
216
|
+
return false;
|
217
|
+
}
|
218
|
+
return true;
|
205
219
|
}
|
206
220
|
|
207
|
-
PleziClient.prototype.
|
208
|
-
|
221
|
+
PleziClient.prototype.emit =
|
222
|
+
function(event, callback, timeout_callback, timeout) {
|
223
|
+
if (!timeout && callback && !this.emit_timeout)
|
224
|
+
timeout = 10;
|
225
|
+
this.___set_failed_timeout(event, timeout_callback, timeout);
|
226
|
+
if (callback) {
|
227
|
+
this[event._EID_] = {callback : callback, event : event};
|
228
|
+
}
|
229
|
+
return this.sendraw(JSON.stringify(event));
|
209
230
|
}
|
210
231
|
|
211
|
-
PleziClient.prototype.
|
212
|
-
|
213
|
-
for (var k in this.add) {
|
214
|
-
combined[k] = this.add[k];
|
215
|
-
};
|
216
|
-
for (var k in event) {
|
217
|
-
combined[k] = event[k];
|
218
|
-
};
|
219
|
-
if (!combined.id) {
|
220
|
-
combined.id = event.event;
|
221
|
-
};
|
222
|
-
var req = new XMLHttpRequest();
|
223
|
-
req.client = this.client;
|
224
|
-
req.json = combined;
|
225
|
-
req.callback = callback;
|
226
|
-
// if(!req.callback) req.callback = this.failed
|
227
|
-
req.onreadystatechange = function() {
|
228
|
-
if (this.readyState != 4) {
|
229
|
-
return;
|
230
|
-
}
|
231
|
-
if (this.status == 200) {
|
232
|
-
try {
|
233
|
-
var res = JSON.parse(this.responseText);
|
234
|
-
this.client.___dispatch(res);
|
235
|
-
} catch (err) {
|
236
|
-
console.error("PleziClient experienced an error parsing the following data (not JSON):",
|
237
|
-
err, this.responseText);
|
238
|
-
}
|
232
|
+
PleziClient.prototype.readyState =
|
233
|
+
function() { return this.ws.readyState; }
|
239
234
|
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
235
|
+
PleziClient.prototype.___ajaj__emit = function(event, callback) {
|
236
|
+
var combined = {};
|
237
|
+
for (var k in this.add) {
|
238
|
+
combined[k] = this.add[k];
|
239
|
+
};
|
240
|
+
for (var k in event) {
|
241
|
+
combined[k] = event[k];
|
242
|
+
};
|
243
|
+
if (!combined.id) {
|
244
|
+
combined.id = event.event;
|
245
|
+
};
|
246
|
+
var req = new XMLHttpRequest();
|
247
|
+
req.client = this.client;
|
248
|
+
req.json = combined;
|
249
|
+
req.callback = callback;
|
250
|
+
// if(!req.callback) req.callback = this.failed
|
251
|
+
req.onreadystatechange = function() {
|
252
|
+
if (this.readyState != 4) {
|
253
|
+
return;
|
245
254
|
}
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
255
|
+
if (this.status == 200) {
|
256
|
+
try {
|
257
|
+
var res = JSON.parse(this.responseText);
|
258
|
+
this.client.___dispatch(res);
|
259
|
+
} catch (err) {
|
260
|
+
console.error(
|
261
|
+
"PleziClient experienced an error parsing the following data (not JSON):",
|
262
|
+
err, this.responseText);
|
263
|
+
}
|
264
|
+
|
265
|
+
} else {
|
266
|
+
if (this.callback) {
|
267
|
+
this.callback(this.json);
|
268
|
+
}
|
252
269
|
}
|
270
|
+
};
|
271
|
+
req.open("POST", this.url, true);
|
272
|
+
req.setRequestHeader("Content-type", "application/json");
|
273
|
+
try {
|
274
|
+
req.send(JSON.stringify(combined));
|
275
|
+
} catch (err) {
|
276
|
+
callback(event);
|
277
|
+
}
|
253
278
|
}
|