plezi 0.12.18 → 0.12.19

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6f4a5058846b5a4228065d751c7bc7019a1b505b
4
- data.tar.gz: 96ab017b8e3d590173fea18733bd018e6f857590
3
+ metadata.gz: 9f3d155857b95dc2f0f7a16994780b14ac4cdf1a
4
+ data.tar.gz: e7a44459df52631f7893c15338da94b4f1e464c7
5
5
  SHA512:
6
- metadata.gz: f696aabd61dfca64e7aad33eb98487421518fa830f31ca2289e852d4f6842d52f9a6cf868879aa9f52332f6eaac3dca7b5c5cc2df04b8dcb966a39be42de7c88
7
- data.tar.gz: cbb3aa6d07560314171fcdab50b63062ac74c13a7c88b9b7f1de85f59d1cd2fb3f230975ca3361f7afa290942032499a8eaaabeeeb7da562d96f7372303cb692
6
+ metadata.gz: c502627effcdaf0b6e4478122349236524f095f76184c0691f3902ac96a52c7d0afe4027e096a69e3ed34f8b1c31fe893cee7fddf8ab50aea2ec21070649b410
7
+ data.tar.gz: daafec0faebb5c8eeecd8be08b47ea15f13935c2c0a1be902cccaebeb0dbf4fb1642174bf9f65ebb6ec74ca15f1ee1a026524202904053779fff279d774e37c4
@@ -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 ""
@@ -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
- unless 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])
70
- return (self.class.has_super_method?(:unknown_event) && ( unknown_event(data) || true)) || write({ event: :err, status: 404, result: "not found", request: data }.to_json)
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 = self.__send__(data['event'.freeze], data)
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
@@ -1,3 +1,3 @@
1
1
  module Plezi
2
- VERSION = "0.12.18"
2
+ VERSION = "0.12.19"
3
3
  end
@@ -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.18"
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
 
@@ -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
- // To open a websocket connection to the current location
4
- // (i.e, "https://example.com/path" => "wss://example.com/path"), use:
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.reconnect = true
23
- // client.reconnect_interval = 250 // sets how long to wait before reconnection attempts. default is 50 ms.
16
+ // client.autoreconnect = true
17
+ // client.reconnect_interval = 250 // sets how long to wait before reconnection attempts. default is 250 ms.
24
18
  //
25
- // The automatic renew flag can be used when creating the client, using:
19
+ // To set up event handling, directly set an `<event name>` callback. i.e., for an event called `chat`:
26
20
  //
27
- // var client = new PleziClient(PleziClient.origin + "/path", true)
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
- // To sent raw websocket data, use the `send` method.
39
- // This might cause disconnetions if Plezi's controller uses `auto_dispatch`.
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.ws = new WebSocket(this.url);
57
- this.ws.owner = this
58
- this.reconnect = false;
59
- this.reconnect_interval = 50
60
- if(reconnect) {this.reconnect = true;}
61
- this.ws.onopen = function(e) {
62
- this.owner.connected = true;
63
- if (this.owner.onopen) { this.owner.onopen(e) }
64
- }
65
- this.ws.onclose = function(e) {
66
- this.connected = false;
67
- if (this.owner.onclose) { this.owner.onclose(e) }
68
- if(this.owner.reconnect) {
69
- setTimeout( function(obj) {
70
- obj.connected = NaN;
71
- obj.ws = new Websocket(obj.url);
72
- obj.ws.owner = obj
73
- }, this.reconnect_interval, this.owner);
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 ( (msg.event) && (this.owner['on' + msg.event])) {
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(err)
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.reconnect = false;
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.send = function(data) {
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(data) {
109
- return this.send( JSON.stringify(data) );
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; }
@@ -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='/assets/plezi_client.js'></script>
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.onalert = function(data) {
36
+ connection.alert = function(data) {
37
37
  alert(JSON.stringify(data));
38
38
  console.log(data);
39
39
  }
40
- // connection.onerr = function(data) {
41
- // alert("Error: " + JSON.stringify(data));
42
- // console.log(data);
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 assets: File.expand_path(File.join('..','..', 'resources'), __FILE__)
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.18
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-30 00:00:00.000000000 Z
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.18
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.18
26
+ version: 0.1.19
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement