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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 67b08fc4fb11066038232a931493efec82a441a9
4
- data.tar.gz: 2027a428e55e2d2ae9dbb071f10cdc0db5f5ee07
3
+ metadata.gz: 393d101919837f0ce957673f30cf4afa858977bb
4
+ data.tar.gz: 5e23ca2e9ddd479fefd37c0747c246a924f6c755
5
5
  SHA512:
6
- metadata.gz: 3b886af9e5a9a93fe98e4f80d336bb6fd472f28dcb208ec3182a9c96f7d364c6c0adfc974cbc505e92f3a3832dd3bd51ac1dc54f642cb10fcb8210b076e6830c
7
- data.tar.gz: 48598ce1bc2dc17f13bc4d8917cfa07dc38420c43bfb50407b36d8190c4de362d49a7ec8c42a160ff9123fb72e67790f21a2281597211757f9e4ff69fe1e68ea
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 for any reading or writing from the cache.
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 acess their process's cache, but each process runs in a different memory space, so they can't share.
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.has_super_method?(data['event'.freeze] = data['event'.freeze].to_s.to_sym) && self.class.instance_variable_get(:@allow_dispatch).include?(data['event'.freeze])
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)
@@ -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["_method"] == 'delete') and request.params\[:id] exists
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 ( self.class.placebo? ? true : we.write(ws.data)) if :method == :to_client
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
- @@uuid_cutoff ||= Plezi::Settings.uuid.length
233
- _inner_broadcast method: method_name, data: args, target: target_uuid[@@uuid_cutoff..-1], to_server: target_uuid[0...@@uuid_cutoff], type: :all
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
@@ -1,3 +1,3 @@
1
1
  module Plezi
2
- VERSION = "0.12.20"
2
+ VERSION = "0.12.21"
3
3
  end
@@ -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
- if (this.owner.log_events) {console.log(msg)}
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.owner[msg.event])) {
72
- this.owner[msg.event](msg);
73
- } else if ( (msg.event) && (this.owner['on' + msg.event])) {
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.owner['on' + msg.event](msg);
91
+ this['on' + msg.event](msg);
77
92
  } else
78
93
  {
79
- if (this.owner['unknown'] && (msg.event != '_ack_') ) {this.owner['unknown'](msg)};
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.ontimeout(event)
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
@@ -21,6 +21,9 @@ class DispatchTest
21
21
  def _hidden
22
22
  {event: :failed, data: :forbidden}.to_json
23
23
  end
24
+ def html
25
+ {event: 'html', data: 'only'}.to_json
26
+ end
24
27
  def test data = {}
25
28
  data[:data] ||= "test"
26
29
  {event: 'alert', data: data[:data]}.to_json
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
- ws2 << "to: #{data.match(/^uuid: ([^s]*)/)[1]}"
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.20
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-04 00:00:00.000000000 Z
11
+ date: 2015-12-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: iodine