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 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