plezi 0.12.20 → 0.12.21
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +9 -3
- data/lib/plezi/handlers/controller_core.rb +1 -7
- data/lib/plezi/handlers/stubs.rb +1 -1
- data/lib/plezi/handlers/ws_object.rb +22 -3
- data/lib/plezi/version.rb +1 -1
- data/resources/plezi_client.js +64 -7
- data/test/dispatch +3 -0
- data/test/plezi_tests.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 393d101919837f0ce957673f30cf4afa858977bb
|
4
|
+
data.tar.gz: 5e23ca2e9ddd479fefd37c0747c246a924f6c755
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 855bc07cedb86d69f9787f6740a8f3397140487d9f00d5eb7fbfbafbc7bf912e8a9719b44ed0d683ea1370a115984f83c6097abda71b3fc6a10a83d68226498d
|
7
|
+
data.tar.gz: 1ca4b13dd3ca651514b5822c1265bff122d1b0d791cab76a841926139b8b93c655b2107611781408399c9b224ce0fdc892f392da1bb0db2f1f96a44a5de40963
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,16 @@
|
|
2
2
|
|
3
3
|
***
|
4
4
|
|
5
|
+
Change log v.0.12.21
|
6
|
+
|
7
|
+
**Update**: Auto-Dispatch:
|
8
|
+
|
9
|
+
* method whitlisting process was updates. Method arity is now reviewed, so that only methods that accept one or more parameters are recognized as websocket auto-dispatch methods (allowing for Http only methods without throwing exceptions).
|
10
|
+
|
11
|
+
* client now adds helper methods for AJAJ (like AJAX, but JSON instead of XML). The javascript client supports the `client.ajaj.emit` to send AJAJ requests and the `client.ajaj.auto=true` to automatically fallback on AJAJ after timeout (unless a custom callback is defined for the specific `emit`).
|
12
|
+
|
13
|
+
***
|
14
|
+
|
5
15
|
Change log v.0.12.20
|
6
16
|
|
7
17
|
**Fix (Client)**: The onopen callback is now called (the issue was related to a typo in the name of the onopen callback).
|
data/README.md
CHANGED
@@ -666,17 +666,23 @@ Plezi and Iodine are written for Ruby versions 2.1.0 or greater (or API compatib
|
|
666
666
|
|
667
667
|
## Who's afraid of multi-threading?
|
668
668
|
|
669
|
+
Let's start with the obvious, **if** your code is short and efficient (no blocking tasks), it is best to run Plezi (Iodine) in a single threaded mode - you get better performance AND safer code, **as long as there are no blocking tasks**:
|
670
|
+
|
671
|
+
Plezi.threads = 1
|
672
|
+
|
673
|
+
But... most applications will naturally have blocking tasks, such as database queries etc'. This is why...:
|
674
|
+
|
669
675
|
Plezi builds on Iodine's concept of "connection locking", meaning that your controllers shouldn't be acessed by more than one thread at the same time.
|
670
676
|
|
671
677
|
This allows you to run Plezi as a multi-threaded (and even multi-process) application as long as your controllers don't change or set any global data... Readeing global data after it was set during initialization is totally fine, just not changing or setting it...
|
672
678
|
|
673
679
|
But wait, global data is super important, right?
|
674
680
|
|
675
|
-
Well, sometimes it is. And although it's a better practice to avoide storing any global data in global variables, sometimes storing stuff in the global space is exactly what we need.
|
681
|
+
Well, sometimes it is. And although it's a better practice to avoide storing any global data in global variables (databases are usually thread safe storage places), sometimes storing stuff in the global space is exactly what we need.
|
676
682
|
|
677
683
|
The solution is simple - if you can't use persistent databases with thread-safe libraries (i.e. Sequel / ActiveRecord / Redis, etc'), use Plezi's global cache storage (see Plezi::Cache).
|
678
684
|
|
679
|
-
Plezi's global cache storage is a memory based storage protected by a mutex
|
685
|
+
Plezi's global cache storage is a local memory based storage protected by a mutex whenever reading or writing from the cache.
|
680
686
|
|
681
687
|
So... these are protected:
|
682
688
|
|
@@ -713,7 +719,7 @@ However, the following is unsafe:
|
|
713
719
|
global_hash[:change] = "NOT safe"
|
714
720
|
|
715
721
|
|
716
|
-
\* be aware, if using Plezi in as a multi-process application, that each process has it's own cache and that processes can't share the cache. The different threads in each of the processes will be able to
|
722
|
+
\* be aware, if using Plezi in as a multi-process application, that each process has it's own cache and that processes can't share the cache. The different threads in each of the processes will be able to access their process's cache, but each process runs in a different memory space, so they can't share.
|
717
723
|
|
718
724
|
## Contributing
|
719
725
|
|
@@ -60,19 +60,13 @@ module Plezi
|
|
60
60
|
rescue
|
61
61
|
return close
|
62
62
|
end
|
63
|
-
self.class.instance_variable_set :@allow_dispatch, (( self.class.instance_methods - (Class.new.instance_methods +
|
64
|
-
Plezi::Base::WSObject::InstanceMethods.instance_methods +
|
65
|
-
Plezi::Base::WSObject::SuperInstanceMethods.instance_methods +
|
66
|
-
Plezi::ControllerMagic::InstanceMethods.instance_methods +
|
67
|
-
Plezi::Base::ControllerCore::InstanceMethods.instance_methods +
|
68
|
-
[:before, :after, :initialize, :unknown , :unknown_event]) ).delete_if {|m| m.to_s[0] == '_'}).to_set unless self.class.instance_variable_get(:@allow_dispatch)
|
69
63
|
Plezi::Base::Helpers.make_hash_accept_symbols data
|
70
64
|
ret = nil
|
71
65
|
begin
|
72
66
|
if data['_EID_'.freeze]
|
73
67
|
write "{\"event\":\"_ack_\",\"_EID_\":#{data['_EID_'.freeze]}}"
|
74
68
|
end
|
75
|
-
if self.class.
|
69
|
+
if self.class.has_auto_dispatch_method?(data['event'.freeze] = data['event'.freeze].to_s.to_sym)
|
76
70
|
ret = self.__send__(data['event'.freeze], data)
|
77
71
|
else
|
78
72
|
ret = (self.class.has_super_method?(:unknown) && ( unknown(data) || true)) || (self.class.has_super_method?(:unknown_event) && Iodine.warn('Auto-Dispatch API updated: use `unknown` instead of `unknown_event`') && ( unknown_event(data) || true)) || ({ event: :err, status: 404, result: "not found", request: data }.to_json)
|
data/lib/plezi/handlers/stubs.rb
CHANGED
@@ -46,7 +46,7 @@ module Plezi
|
|
46
46
|
"update called - updating #{params[:id]}"
|
47
47
|
end
|
48
48
|
|
49
|
-
# called when request is DELETE (or params[
|
49
|
+
# called when request is DELETE (or params[:_method] == 'delete') and request.params\[:id] exists
|
50
50
|
def delete
|
51
51
|
"delete called - deleting object #{params[:id]}"
|
52
52
|
end
|
@@ -77,7 +77,7 @@ module Plezi
|
|
77
77
|
return false
|
78
78
|
end
|
79
79
|
return false if data[:type] && data[:type] != :all && !self.is_a?(data[:type])
|
80
|
-
# return (
|
80
|
+
# return (data[:data].each {|e| emit(e)}) if data[:method] == :emit
|
81
81
|
return ((data[:type] == :all) ? false : (raise "Broadcasting recieved but no method can handle it - dump:\r\n #{data.to_s}") ) unless self.class.has_super_method?(data[:method])
|
82
82
|
self.__send__(data[:method], *data[:data])
|
83
83
|
end
|
@@ -103,6 +103,14 @@ module Plezi
|
|
103
103
|
(@ws_io || @response) << data
|
104
104
|
end
|
105
105
|
|
106
|
+
# # @!visibility public
|
107
|
+
# # A helper method for easily sending JSON data. Accepts a Hash that will be translated to JSON and sent to the client as a JSON string.
|
108
|
+
# #
|
109
|
+
# # This method is available as a broadcast event.
|
110
|
+
# def emit event
|
111
|
+
# write event.to_json
|
112
|
+
# end
|
113
|
+
|
106
114
|
# @!visibility public
|
107
115
|
# Closes the connection
|
108
116
|
def close
|
@@ -153,9 +161,11 @@ module Plezi
|
|
153
161
|
@methods_list = nil
|
154
162
|
@exposed_methods_list = nil
|
155
163
|
@super_methods_list = nil
|
164
|
+
@auto_dispatch_list = nil
|
156
165
|
has_method? nil
|
157
166
|
has_exposed_method? nil
|
158
167
|
has_super_method? nil
|
168
|
+
has_auto_dispatch_method? nil
|
159
169
|
end
|
160
170
|
def has_method? method_name
|
161
171
|
@methods_list ||= self.instance_methods.to_set
|
@@ -175,6 +185,15 @@ module Plezi
|
|
175
185
|
@exposed_methods_list ||= ( (self.public_instance_methods - @reserved_methods_list ).delete_if {|m| m.to_s[0] == '_'} ).to_set
|
176
186
|
@exposed_methods_list.include? method_name
|
177
187
|
end
|
188
|
+
def has_auto_dispatch_method? method_name
|
189
|
+
@auto_dispatch_list ||= (( self.instance_methods - (Class.new.instance_methods +
|
190
|
+
Plezi::Base::WSObject::InstanceMethods.instance_methods +
|
191
|
+
Plezi::Base::WSObject::SuperInstanceMethods.instance_methods +
|
192
|
+
Plezi::ControllerMagic::InstanceMethods.instance_methods +
|
193
|
+
Plezi::Base::ControllerCore::InstanceMethods.instance_methods +
|
194
|
+
[:before, :after, :initialize, :unknown , :unknown_event]) ).delete_if {|m| m.to_s[0] == '_' || instance_method(m).arity == 0 }).to_set
|
195
|
+
@auto_dispatch_list.include? method_name
|
196
|
+
end
|
178
197
|
|
179
198
|
protected
|
180
199
|
|
@@ -229,8 +248,8 @@ module Plezi
|
|
229
248
|
# *args:: any arguments that should be passed to the method (IF REDIS IS USED, LIMITATIONS APPLY).
|
230
249
|
def unicast target_uuid, method_name, *args
|
231
250
|
raise 'No target specified for unicasting!' unless target_uuid
|
232
|
-
|
233
|
-
_inner_broadcast method: method_name, data: args, target: target_uuid[
|
251
|
+
@uuid_cutoff ||= Plezi::Settings.uuid.length
|
252
|
+
_inner_broadcast method: method_name, data: args, target: target_uuid[@uuid_cutoff..-1], to_server: target_uuid[0...@uuid_cutoff], type: :all
|
234
253
|
end
|
235
254
|
|
236
255
|
# Use this to multicast an event to ALL websocket connections on EVERY controller, including Placebo controllers.
|
data/lib/plezi/version.rb
CHANGED
data/resources/plezi_client.js
CHANGED
@@ -33,6 +33,13 @@ function PleziClient(url, autoreconnect) {
|
|
33
33
|
}
|
34
34
|
// Connect Websocket
|
35
35
|
this.reconnect();
|
36
|
+
// Setup AJAJ
|
37
|
+
this.ajaj = {};
|
38
|
+
this.ajaj.client = this
|
39
|
+
this.ajaj.url = this.url.replace(/^ws:\/\//i, "http://").replace(/^wss:\/\//i, "https://");
|
40
|
+
this.ajaj.add = {};
|
41
|
+
this.ajaj.emit = this.___ajaj__emit;
|
42
|
+
this.ajaj.auto = false
|
36
43
|
// auto-reconnection
|
37
44
|
this.autoreconnect = false;
|
38
45
|
this.reconnect_interval = 200
|
@@ -66,23 +73,32 @@ PleziClient.prototype.___on_error = function(e) {
|
|
66
73
|
PleziClient.prototype.___on_message = function(e) {
|
67
74
|
try {
|
68
75
|
var msg = JSON.parse(e.data);
|
69
|
-
|
76
|
+
this.owner.___dispatch(msg);
|
77
|
+
} catch(err) {
|
78
|
+
console.error("PleziClient experienced an error parsing the following data (not JSON):",
|
79
|
+
err, e.data)
|
80
|
+
}
|
81
|
+
}
|
82
|
+
PleziClient.prototype.___dispatch = function(msg) {
|
83
|
+
try {
|
84
|
+
if (this.log_events) {console.log(msg)}
|
70
85
|
if ( msg.event == '_ack_') { clearTimeout(msg._EID_) }
|
71
|
-
if ( (msg.event) && (this
|
72
|
-
this
|
73
|
-
} else if ( (msg.event) && (this
|
86
|
+
if ( (msg.event) && (this[msg.event])) {
|
87
|
+
this[msg.event](msg);
|
88
|
+
} else if ( (msg.event) && (this['on' + msg.event])) {
|
74
89
|
console.warn('PleziClient: use a callback called "' + msg.event +
|
75
90
|
'" instead of of "on' + msg.event + '"');
|
76
|
-
this
|
91
|
+
this['on' + msg.event](msg);
|
77
92
|
} else
|
78
93
|
{
|
79
|
-
if (this
|
94
|
+
if (this['unknown'] && (msg.event != '_ack_') ) {this['unknown'](msg)};
|
80
95
|
}
|
81
96
|
} catch(err) {
|
82
97
|
console.error("PleziClient experienced an error while responding to the following onmessage event",
|
83
98
|
err, e)
|
84
99
|
}
|
85
100
|
}
|
101
|
+
|
86
102
|
// Sets a timeout for the websocket message
|
87
103
|
PleziClient.prototype.___set_failed_timeout = function(event, callback, timeout) {
|
88
104
|
if(event._EID_) {return event;};
|
@@ -95,7 +111,12 @@ PleziClient.prototype.___set_failed_timeout = function(event, callback, timeout)
|
|
95
111
|
// Removes the _client_ property from the event and calls
|
96
112
|
// the ontimeout callback within the correct scope
|
97
113
|
PleziClient.prototype.___on_timeout = function(event, client) {
|
98
|
-
client.
|
114
|
+
if (client.ajaj.auto) {
|
115
|
+
if (client.log_events) {console.log("falling back on AJAJ for the event:", event)}
|
116
|
+
client.ajaj.emit(event, client.ontimeout);
|
117
|
+
} else {
|
118
|
+
client.ontimeout(event);
|
119
|
+
}
|
99
120
|
}
|
100
121
|
// The timeout callback
|
101
122
|
PleziClient.prototype.ontimeout = function(event) {
|
@@ -139,3 +160,39 @@ PleziClient.prototype.emit = function(event, callback, timeout) {
|
|
139
160
|
}
|
140
161
|
|
141
162
|
PleziClient.prototype.readyState = function() { return this.ws.readyState; }
|
163
|
+
|
164
|
+
PleziClient.prototype.___ajaj__emit = function(event, callback) {
|
165
|
+
var combined = {}
|
166
|
+
for (var k in this.add) {combined[k] = this.add[k];};
|
167
|
+
for (var k in event) {combined[k] = event[k];};
|
168
|
+
if(!combined.id) {combined.id = event.event;};
|
169
|
+
var req = new XMLHttpRequest();
|
170
|
+
req.client = this.client;
|
171
|
+
req.json = combined;
|
172
|
+
req.callback = callback
|
173
|
+
// if(!req.callback) req.callback = this.failed
|
174
|
+
req.onreadystatechange = function() {
|
175
|
+
if (this.readyState != 4) { return }
|
176
|
+
if (this.status == 200) {
|
177
|
+
try {
|
178
|
+
var res = JSON.parse(this.responseText);
|
179
|
+
this.client.___dispatch(res);
|
180
|
+
} catch(err) {
|
181
|
+
console.error("PleziClient experienced an error parsing the following data (not JSON):",
|
182
|
+
err, this.responseText)
|
183
|
+
}
|
184
|
+
|
185
|
+
} else {
|
186
|
+
if(this.callback) {
|
187
|
+
this.callback(this.json);
|
188
|
+
}
|
189
|
+
}
|
190
|
+
}
|
191
|
+
req.open("POST", this.url ,true);
|
192
|
+
req.setRequestHeader("Content-type", "application/json");
|
193
|
+
try {
|
194
|
+
req.send(JSON.stringify(combined));
|
195
|
+
} catch(err) {
|
196
|
+
callback(event)
|
197
|
+
}
|
198
|
+
}
|
data/test/dispatch
CHANGED
data/test/plezi_tests.rb
CHANGED
@@ -376,7 +376,7 @@ module PleziTestTasks
|
|
376
376
|
end
|
377
377
|
ws3 = Iodine::Http::WebsocketClient.connect("ws://localhost:3000/", on_open: -> { write 'get uuid' } ) do |data|
|
378
378
|
if data.match /uuid: ([^s]*)/
|
379
|
-
|
379
|
+
ws3 << "to: #{data.match(/^uuid: ([^s]*)/)[1]}"
|
380
380
|
puts " * Websocket UUID for unicast testing: #{data.match(/^uuid: ([^s]*)/)[1]}"
|
381
381
|
elsif data == "unicast"
|
382
382
|
puts " * Websocket unicast testing: #{RESULTS[:waiting]} (target received data)"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plezi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.12.
|
4
|
+
version: 0.12.21
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Boaz Segev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-12-
|
11
|
+
date: 2015-12-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: iodine
|