plezi 0.12.18 → 0.12.19
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 +16 -0
- data/bin/plezi +0 -1
- data/lib/plezi/common/api.rb +5 -0
- data/lib/plezi/handlers/controller_core.rb +15 -4
- data/lib/plezi/handlers/ws_object.rb +7 -0
- data/lib/plezi/version.rb +1 -1
- data/plezi.gemspec +1 -1
- data/resources/plezi_client.js +87 -61
- data/test/dispatch +10 -8
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9f3d155857b95dc2f0f7a16994780b14ac4cdf1a
|
|
4
|
+
data.tar.gz: e7a44459df52631f7893c15338da94b4f1e464c7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c502627effcdaf0b6e4478122349236524f095f76184c0691f3902ac96a52c7d0afe4027e096a69e3ed34f8b1c31fe893cee7fddf8ab50aea2ec21070649b410
|
|
7
|
+
data.tar.gz: daafec0faebb5c8eeecd8be08b47ea15f13935c2c0a1be902cccaebeb0dbf4fb1642174bf9f65ebb6ec74ca15f1ee1a026524202904053779fff279d774e37c4
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
***
|
|
4
4
|
|
|
5
|
+
Change log v.0.12.19
|
|
6
|
+
|
|
7
|
+
**Updates**: Auto-Dispatch updates:
|
|
8
|
+
|
|
9
|
+
* Updated the Auto-Dispatch API conventions, so that the client and the server conventions are the same (both map events to methods with the same name (without the `on` prefix) and map unknown events to the `unknown` callback).
|
|
10
|
+
|
|
11
|
+
* Auto-Dispatch callbacks can return a Hash as well as a String when the return value is meant to be written to the websockets.
|
|
12
|
+
|
|
13
|
+
* Auto-Dispatch sends an `_ack_` event whenever an event contains an event ID (`_EID_`) property.
|
|
14
|
+
|
|
15
|
+
* Auto-Dispatch client's `emit` supports timeout callbacks (for easier fallback to AJAX).
|
|
16
|
+
|
|
17
|
+
* A `:client` route allows serving Auto-Dispatch's updated client rather than the original client provided in the template. This allows the application to serve the updated client when Plezi is updated.
|
|
18
|
+
|
|
19
|
+
***
|
|
20
|
+
|
|
5
21
|
Change log v.0.12.18
|
|
6
22
|
|
|
7
23
|
**Feature**: Auto-dispatching allows, when enabled, to automatically map JSON websocket messages to controller methods, according to their `event` property. Also, when using the auto-dispatch, Plezi will automatically send the returned value for dispatch methods that return a String (just like when using Http). This means that unifying Websocket and RESTful APIs is now easier than ever.
|
data/bin/plezi
CHANGED
|
@@ -50,7 +50,6 @@ end
|
|
|
50
50
|
if ARGV[0] == 'new' || ARGV[0] == 'n' || ARGV[0] == "force" || ARGV[0] == 'mini' || ARGV[0] == 'm'
|
|
51
51
|
#########
|
|
52
52
|
## set up building environment
|
|
53
|
-
NO_PLEZI_AUTO_START = true
|
|
54
53
|
ARGV[1] = ARGV[1].gsub /[^a-zA-Z0-9]/, '_'
|
|
55
54
|
if Dir.exists?(ARGV[1]) && ARGV[0] != "force"
|
|
56
55
|
puts ""
|
data/lib/plezi/common/api.rb
CHANGED
|
@@ -13,6 +13,11 @@ module Plezi
|
|
|
13
13
|
|
|
14
14
|
# adds a route to the last server created
|
|
15
15
|
def route(path, controller = nil, &block)
|
|
16
|
+
if controller == :client
|
|
17
|
+
client_path = File.expand_path(File.join('..','..','..','..','resources','plezi_client.js'), __FILE__)
|
|
18
|
+
controller = nil
|
|
19
|
+
block = Proc.new { Plezi.cache_needs_update?(client_path) ? Plezi.reload_file(client_path) : Plezi.load_file(client_path) }
|
|
20
|
+
end
|
|
16
21
|
::Plezi::Base::HTTPRouter.add_route path, controller, &block
|
|
17
22
|
end
|
|
18
23
|
|
|
@@ -56,6 +56,7 @@ module Plezi
|
|
|
56
56
|
end
|
|
57
57
|
begin
|
|
58
58
|
data = JSON.parse data
|
|
59
|
+
return close unless data.is_a?(Hash)
|
|
59
60
|
rescue
|
|
60
61
|
return close
|
|
61
62
|
end
|
|
@@ -64,12 +65,22 @@ module Plezi
|
|
|
64
65
|
Plezi::Base::WSObject::SuperInstanceMethods.instance_methods +
|
|
65
66
|
Plezi::ControllerMagic::InstanceMethods.instance_methods +
|
|
66
67
|
Plezi::Base::ControllerCore::InstanceMethods.instance_methods +
|
|
67
|
-
[:before, :after, :initialize]) ).delete_if {|m| m.to_s[0] == '_'}).to_set unless self.class.instance_variable_get(:@allow_dispatch)
|
|
68
|
+
[:before, :after, :initialize, :unknown , :unknown_event]) ).delete_if {|m| m.to_s[0] == '_'}).to_set unless self.class.instance_variable_get(:@allow_dispatch)
|
|
68
69
|
Plezi::Base::Helpers.make_hash_accept_symbols data
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
ret = nil
|
|
71
|
+
begin
|
|
72
|
+
if data['_EID_'.freeze]
|
|
73
|
+
write "{\"event\":\"_ack_\",\"_EID_\":#{data['_EID_'.freeze]}}"
|
|
74
|
+
end
|
|
75
|
+
if self.class.has_super_method?(data['event'.freeze] = data['event'.freeze].to_s.to_sym) && self.class.instance_variable_get(:@allow_dispatch).include?(data['event'.freeze])
|
|
76
|
+
ret = self.__send__(data['event'.freeze], data)
|
|
77
|
+
else
|
|
78
|
+
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)
|
|
79
|
+
end
|
|
80
|
+
rescue ArgumentError => e
|
|
81
|
+
Iodine.error "Auto-Dispatch Error for event :#{data['event'.freeze]} - #{e.message}"
|
|
71
82
|
end
|
|
72
|
-
ret =
|
|
83
|
+
ret = ret.to_json if ret.is_a?(Hash)
|
|
73
84
|
write(ret) if ret.is_a?(String)
|
|
74
85
|
end
|
|
75
86
|
|
|
@@ -103,6 +103,13 @@ module Plezi
|
|
|
103
103
|
(@ws_io || @response) << data
|
|
104
104
|
end
|
|
105
105
|
|
|
106
|
+
# @!visibility public
|
|
107
|
+
# Closes the connection
|
|
108
|
+
def close
|
|
109
|
+
# @request[:io] contains the Websockets Protocol instance
|
|
110
|
+
(@ws_io || @request[:io]).go_away
|
|
111
|
+
end
|
|
112
|
+
|
|
106
113
|
# @!visibility public
|
|
107
114
|
# Performs a websocket unicast to the specified target.
|
|
108
115
|
def unicast target_uuid, method_name, *args
|
data/lib/plezi/version.rb
CHANGED
data/plezi.gemspec
CHANGED
|
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
19
|
spec.require_paths = ["lib"]
|
|
20
20
|
|
|
21
|
-
spec.add_dependency "iodine", "~> 0.1.
|
|
21
|
+
spec.add_dependency "iodine", "~> 0.1.19"
|
|
22
22
|
spec.add_development_dependency "bundler", "~> 1.7"
|
|
23
23
|
spec.add_development_dependency "rake", "~> 10.0"
|
|
24
24
|
|
data/resources/plezi_client.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// This is a commonly used structure for WebSocket messanging.
|
|
2
|
+
// The documentation is available on the www.plezi.io website:
|
|
3
|
+
// http://www.plezi.io/docs/websockets#websocket-json-auto-dispatch
|
|
2
4
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
+
// Basics:
|
|
6
|
+
// To open a websocket connection to the current location:
|
|
5
7
|
//
|
|
6
8
|
// var client = new PleziClient()
|
|
7
9
|
//
|
|
@@ -9,104 +11,128 @@
|
|
|
9
11
|
//
|
|
10
12
|
// var client = new PleziClient(PleziClient.origin + "/path")
|
|
11
13
|
//
|
|
12
|
-
// i.e., to open a connection to the root ("/"), use:
|
|
13
|
-
//
|
|
14
|
-
// var client = new PleziClient(PleziClient.origin + "/")
|
|
15
|
-
//
|
|
16
|
-
// To open a connection to a different URL or path, use:
|
|
17
|
-
//
|
|
18
|
-
// var client = new PleziClient("ws://full.url.com/path")
|
|
19
|
-
//
|
|
20
14
|
// To automatically renew the connection when disconnections are reported by the browser, use:
|
|
21
15
|
//
|
|
22
|
-
// client.
|
|
23
|
-
// client.reconnect_interval = 250 // sets how long to wait before reconnection attempts. default is
|
|
16
|
+
// client.autoreconnect = true
|
|
17
|
+
// client.reconnect_interval = 250 // sets how long to wait before reconnection attempts. default is 250 ms.
|
|
24
18
|
//
|
|
25
|
-
//
|
|
19
|
+
// To set up event handling, directly set an `<event name>` callback. i.e., for an event called `chat`:
|
|
26
20
|
//
|
|
27
|
-
//
|
|
28
|
-
// client.reconnect_interval = 250 // Or use the default 50 ms.
|
|
29
|
-
//
|
|
30
|
-
// To set up event handling, directly set an `on<event name>` callback. i.e., for an event called `chat`:
|
|
31
|
-
//
|
|
32
|
-
// client.onchat = function(event) { "..." }
|
|
21
|
+
// client.chat = function(event) { "..." }
|
|
33
22
|
//
|
|
34
23
|
// To sent / emit event in JSON format, use the `emit` method:
|
|
35
24
|
//
|
|
36
25
|
// client.emit({event: "chat", data: "the message"})
|
|
37
26
|
//
|
|
38
|
-
|
|
39
|
-
//
|
|
40
|
-
// i.e. sending a string:
|
|
41
|
-
//
|
|
42
|
-
// client.send("string")
|
|
43
|
-
//
|
|
44
|
-
// Manually closing the connection will prevent automatic reconnection:
|
|
45
|
-
//
|
|
46
|
-
// client.close()
|
|
47
|
-
//
|
|
48
|
-
function PleziClient(url, reconnect) {
|
|
49
|
-
this.connected = NaN;
|
|
27
|
+
function PleziClient(url, autoreconnect) {
|
|
28
|
+
// Set URL
|
|
50
29
|
if(url) {
|
|
51
30
|
this.url = url
|
|
52
31
|
} else {
|
|
53
32
|
this.url = PleziClient.origin + window.location.pathname
|
|
54
33
|
}
|
|
55
|
-
|
|
56
|
-
this.
|
|
57
|
-
|
|
58
|
-
this.
|
|
59
|
-
this.reconnect_interval =
|
|
60
|
-
|
|
61
|
-
this.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
34
|
+
// Connect Websocket
|
|
35
|
+
this.reconnect();
|
|
36
|
+
// auto-reconnection
|
|
37
|
+
this.autoreconnect = false;
|
|
38
|
+
this.reconnect_interval = 200
|
|
39
|
+
// the timeout for a message ack receipt
|
|
40
|
+
this.emit_timeout = false
|
|
41
|
+
// Set the autoreconnect property
|
|
42
|
+
if(autoreconnect) {this.autoreconnect = true;}
|
|
43
|
+
}
|
|
44
|
+
// The Websocket onopen callback
|
|
45
|
+
PleziClient.prototype.___on_open = function(e) {
|
|
46
|
+
this.owner.connected = true;
|
|
47
|
+
if (this.owner.onopen) { this.owner.onopen(e) }
|
|
48
|
+
}
|
|
49
|
+
// The Websocket onclose callback
|
|
50
|
+
PleziClient.prototype.___on_close = function(e) {
|
|
51
|
+
this.owner.connected = false;
|
|
52
|
+
if (this.owner.onclose) { this.owner.onclose(e) }
|
|
53
|
+
if(this.owner.autoreconnect) {
|
|
54
|
+
setTimeout( function(obj) {
|
|
55
|
+
obj.reconnect();
|
|
56
|
+
}, this.owner.reconnect_interval, this.owner);
|
|
75
57
|
}
|
|
76
|
-
this.ws.onerror = function(e) { if (this.owner.onerror) {this.owner.onerror(e)} }
|
|
77
|
-
this.ws.onmessage = this.___on_message
|
|
78
58
|
}
|
|
79
|
-
|
|
59
|
+
// The Websocket onerror callback
|
|
60
|
+
PleziClient.prototype.___on_error = function(e) {
|
|
61
|
+
if (this.owner.onerror) {this.owner.onerror(e)}
|
|
62
|
+
}
|
|
63
|
+
// The Websocket onmessage callback
|
|
80
64
|
PleziClient.prototype.___on_message = function(e) {
|
|
81
65
|
try {
|
|
82
66
|
var msg = JSON.parse(e.data);
|
|
83
|
-
if (
|
|
67
|
+
if ( msg.event == '_ack_') { clearTimeout(msg._EID_) }
|
|
68
|
+
if ( (msg.event) && (this.owner[msg.event])) {
|
|
69
|
+
this.owner[msg.event](msg);
|
|
70
|
+
} else if ( (msg.event) && (this.owner['on' + msg.event])) {
|
|
71
|
+
console.warn('PleziClient: use a callback called "' + msg.event +
|
|
72
|
+
'" instead of of "on' + msg.event + '"');
|
|
84
73
|
this.owner['on' + msg.event](msg);
|
|
85
74
|
} else
|
|
86
75
|
{
|
|
87
|
-
if (this.owner['unknown']) {this.owner['unknown'](msg)};
|
|
76
|
+
if (this.owner['unknown'] && (msg.event != '_ack_') ) {this.owner['unknown'](msg)};
|
|
88
77
|
}
|
|
89
78
|
} catch(err) {
|
|
90
|
-
console.error(
|
|
79
|
+
console.error("PleziClient experienced an error while responding to the following onmessage event",
|
|
80
|
+
err, e)
|
|
91
81
|
}
|
|
92
82
|
}
|
|
83
|
+
// Sets a timeout for the websocket message
|
|
84
|
+
PleziClient.prototype.___set_failed_timeout = function(event, callback, timeout) {
|
|
85
|
+
if(event._EID_) {return event;};
|
|
86
|
+
if(!timeout) { timeout = this.emit_timeout; };
|
|
87
|
+
if(!callback) { callback = this.___on_timeout; };
|
|
88
|
+
if(!timeout) { return event; };
|
|
89
|
+
event._EID_ = setTimeout(callback, timeout, event, this);
|
|
90
|
+
return event;
|
|
91
|
+
}
|
|
92
|
+
// Removes the _client_ property from the event and calls
|
|
93
|
+
// the ontimeout callback within the correct scope
|
|
94
|
+
PleziClient.prototype.___on_timeout = function(event, client) {
|
|
95
|
+
client.ontimeout(event)
|
|
96
|
+
}
|
|
97
|
+
// The timeout callback
|
|
98
|
+
PleziClient.prototype.ontimeout = function(event) {
|
|
99
|
+
console.warn("Timeout reached - it's assumed the connection was lost " +
|
|
100
|
+
"and the following event was ignored by the server:", event);
|
|
101
|
+
console.log(this);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
PleziClient.prototype.reconnect = function() {
|
|
105
|
+
this.connected = NaN;
|
|
106
|
+
this.ws = new WebSocket(this.url);
|
|
107
|
+
// lets us access the client from the callbacks
|
|
108
|
+
this.ws.owner = this
|
|
109
|
+
// The Websocket onopen callback
|
|
110
|
+
this.ws.on_open = this.___on_open
|
|
111
|
+
// The Websocket onclose callback
|
|
112
|
+
this.ws.onclose = this.___on_close
|
|
113
|
+
// The Websocket onerror callback
|
|
114
|
+
this.ws.onerror = this.___on_error
|
|
115
|
+
// The Websocket onmessage callback
|
|
116
|
+
this.ws.onmessage = this.___on_message
|
|
117
|
+
}
|
|
93
118
|
|
|
94
119
|
PleziClient.prototype.close = function() {
|
|
95
|
-
this.
|
|
120
|
+
this.autoreconnect = false;
|
|
96
121
|
this.ws.close();
|
|
97
122
|
}
|
|
98
123
|
|
|
99
124
|
PleziClient.origin = (window.location.protocol.match(/https/) ? 'wws' : 'ws') + '://' + window.location.hostname + (window.location.port == '' ? '' : (':' + window.location.port) );
|
|
100
125
|
|
|
101
|
-
PleziClient.prototype.
|
|
126
|
+
PleziClient.prototype.sendraw = function(data) {
|
|
102
127
|
if (this.ws.readyState != 1) { return false; }
|
|
103
128
|
this.ws.send(data);
|
|
104
129
|
if (this.ws.readyState != 1) { return false; }
|
|
105
130
|
return true
|
|
106
131
|
}
|
|
107
132
|
|
|
108
|
-
PleziClient.prototype.emit = function(
|
|
109
|
-
|
|
133
|
+
PleziClient.prototype.emit = function(event, callback, timeout) {
|
|
134
|
+
this.___set_failed_timeout(event, callback, timeout)
|
|
135
|
+
return this.sendraw( JSON.stringify(event) );
|
|
110
136
|
}
|
|
111
137
|
|
|
112
138
|
PleziClient.prototype.readyState = function() { return this.ws.readyState; }
|
data/test/dispatch
CHANGED
|
@@ -25,22 +25,23 @@ class DispatchTest
|
|
|
25
25
|
data[:data] ||= "test"
|
|
26
26
|
{event: 'alert', data: data[:data]}.to_json
|
|
27
27
|
end
|
|
28
|
-
def index
|
|
29
|
-
%q{<html><head><script src='/
|
|
28
|
+
def index event = nil
|
|
29
|
+
%q{<html><head><script src='/client.js'></script>
|
|
30
30
|
</head>
|
|
31
31
|
<body>
|
|
32
32
|
<button onclick='connection.emit({event: "test", data: "Woohooo!"});' value='test'>Test!</button>
|
|
33
33
|
<script>
|
|
34
34
|
document.body.onload = function() {
|
|
35
35
|
connection = new PleziClient();
|
|
36
|
-
connection.
|
|
36
|
+
connection.alert = function(data) {
|
|
37
37
|
alert(JSON.stringify(data));
|
|
38
38
|
console.log(data);
|
|
39
39
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
connection.err = function(data) {
|
|
41
|
+
alert("Error: " + JSON.stringify(data));
|
|
42
|
+
console.log(data);
|
|
43
|
+
}
|
|
44
|
+
connection.emit_timeout = 3000;
|
|
44
45
|
connection.unknown = function(data) {
|
|
45
46
|
alert("Unknown event: " + JSON.stringify(data));
|
|
46
47
|
console.log(data);
|
|
@@ -49,5 +50,6 @@ document.body.onload = function() {
|
|
|
49
50
|
</script></body></html>}
|
|
50
51
|
end
|
|
51
52
|
end
|
|
52
|
-
host
|
|
53
|
+
host
|
|
54
|
+
route '/client.js', :client
|
|
53
55
|
route '/', DispatchTest
|
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.19
|
|
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-
|
|
11
|
+
date: 2015-12-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: iodine
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.1.
|
|
19
|
+
version: 0.1.19
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 0.1.
|
|
26
|
+
version: 0.1.19
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: bundler
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|