plezi 0.8.5 → 0.8.6

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: 7f6b05d42e0915681853564afa40dde6362c11a5
4
- data.tar.gz: 4acd945c253d4eb550f4c3d102494703c5aac507
3
+ metadata.gz: c7c44156e8d07feee948374172192f5bbaafa806
4
+ data.tar.gz: a8a7badafd82d78e01a20d3ccf55e8bcc1dbd20a
5
5
  SHA512:
6
- metadata.gz: c8e251f2f1e9b358612406cc13df2d59a3f601703000999a3424072c712ff2e6ebd0a3a2555dadaf5d8a11b26cc06ca75fea117b046b1b11967be99d27cd3e75
7
- data.tar.gz: 5637b576b93d7672f83b3f5e7943e993aeeee2f6fdceb3125fe4e28df67c2830ca12959158c6043e0a5be5a608f0c26a341285fcaa7d53914dac5746acaf0095
6
+ metadata.gz: 54db5b96460193896c8feacf4db749ea68132b0659007b93b54633bc4e99e45689906bc666df1517aa644e2f5f573d63d4b0364a4723ddc621c13ec7eae6dd53
7
+ data.tar.gz: 1e2426a2844334deb000f49381f6dad3ee1c89feaff3feb32944ba5ec7ad03a37c44985de8a4d50bf7ab7c88479893d180ca871ee782c2e22a425359876cac3f
data/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ***
4
4
 
5
+ Change log v.0.8.6
6
+
7
+ **fix**: fixed an issue with the plezi helper script that prevented the script from starting the Plezi app or Plezi console.
8
+
9
+ **feature**: Unicasting allows you to target a specific Websocket connection using a unique identifier (UUID). Use the controllers `#unicast(target_uuid, mathod_name, *args) to target a specific client. Automatically uses Radis, if Radis is set up, to unicast across processes.
10
+
11
+ ***
12
+
5
13
  Change log v.0.8.5
6
14
 
7
15
  **feature**: Plezi now includes a very simple Websocket Client (no support for cookies). It's used for testing the integrity of the Plezi Framework and could be used to test the Plezi apps.
data/README.md CHANGED
@@ -36,13 +36,13 @@ to create a new barebones app using the Plezi framework, run from terminal:
36
36
  That's it, now you have a ready to use basic web server (with some demo code), just run it:
37
37
 
38
38
  $ cd appname
39
- $ ./appname.rb # ( or: plezi s )
39
+ $ ./appname # ( or: plezi s )
40
40
 
41
41
  now go, in your browser, to: [http://localhost:3000/](http://localhost:3000/)
42
42
 
43
43
  the default first port for the app is 3000. you can set the first port to listen to by using the `-p ` option (make sure you have permissions for the requested port):
44
44
 
45
- $ ./appname.rb -p 80
45
+ $ ./appname -p 80
46
46
 
47
47
  you now have a smart framework app that will happily eat any gem you feed it. it responds extra well to Haml, Sass and Coffee-Script, which you can enable in it's Gemfile.
48
48
 
data/bin/plezi CHANGED
@@ -264,10 +264,10 @@ if ARGV[0] == 'new' || ARGV[0] == 'n' || ARGV[0] == "force"
264
264
  template.build
265
265
  elsif ARGV[0] == 'server' || ARGV[0] == 'start' || ARGV[0] == 's'
266
266
  ARGV.shift
267
- load File.expand_path(Dir["."][0], (File.expand_path(Dir["."][0]).split(/[\\\/]/).last) ) rescue load( File.expand_path(Dir["."][0], (File.expand_path(Dir["."][0]).split(/[\\\/]/).last + '.rb') ) )
267
+ load File.expand_path(Dir["."][0], (File.expand_path(Dir["."][0]).split(/[\\\/]/).last) ) rescue load( File.expand_path(Dir["."][0], (File.expand_path(Dir["."][0]).split(/[\\\/]/).last ) ) )
268
268
  elsif ARGV[0] == 'console' || ARGV[0] == 'c'
269
269
  NO_PLEZI_AUTO_START ||= true
270
- load File.expand_path(Dir["."][0], (File.expand_path(Dir["."][0]).split(/[\\\/]/).last) ) rescue load( File.expand_path(Dir["."][0], (File.expand_path(Dir["."][0]).split(/[\\\/]/).last + '.rb') ) )
270
+ load File.expand_path(Dir["."][0], (File.expand_path(Dir["."][0]).split(/[\\\/]/).last) ) rescue load( File.expand_path(Dir["."][0], (File.expand_path(Dir["."][0]).split(/[\\\/]/).last) ) )
271
271
  ARGV.clear
272
272
  IRB.setup nil
273
273
  IRB.conf[:MAIN_CONTEXT] = IRB::Irb.new.context
@@ -28,11 +28,14 @@ module Plezi
28
28
  end
29
29
 
30
30
  def stop_and_wait
31
- @workers_lock.synchronize { @workers.each {|w| w.stop }; @workers.each {|w| w.join }; @workers.clear; Worker.reset_wait}
31
+ @workers.each {|w| w.stop }
32
+ @workers.each {|w| w.join }
33
+ @workers_lock.synchronize { @workers.clear }
32
34
  end
33
35
 
34
36
  def stop
35
- @workers_lock.synchronize { @workers.each {|w| w.stop } ; @workers.clear; Worker.reset_wait}
37
+ @workers.each {|w| w.stop }
38
+ @workers_lock.synchronize { @workers.clear;}
36
39
  end
37
40
 
38
41
  def running?
@@ -48,9 +51,9 @@ module Plezi
48
51
  SHUTDOWN_CALLBACKS = []
49
52
  # runs the shutdown queue
50
53
  def shutdown
51
- stop_and_wait rescue false
52
54
  old_timeout = io_timeout
53
55
  io_timeout = 0.001
56
+ stop_and_wait rescue false
54
57
  run
55
58
  io_timeout = old_timeout
56
59
  do_job while do_job
@@ -60,7 +60,7 @@ module Plezi
60
60
  end
61
61
  # clears all timers
62
62
  def clear_timers
63
- TIMERS_LOCK.synchronize { TIMERS.clear }
63
+ TIMERS.clear
64
64
  end
65
65
 
66
66
  end
@@ -9,6 +9,7 @@ module Plezi
9
9
  @thread = Thread.new { EventMachine.run wait until @stop }
10
10
  end
11
11
  def stop
12
+ @instances = -1
12
13
  @stop = true
13
14
  end
14
15
  def join
@@ -27,9 +28,6 @@ module Plezi
27
28
  @instances += 1 if @instances < 7
28
29
  @primes[@instances] / 10.0
29
30
  end
30
- def self.reset_wait
31
- @instances = -1
32
- end
33
31
  end
34
32
  end
35
33
  end
@@ -44,9 +44,14 @@ module Plezi
44
44
  # the parameters used to create the host (the parameters passed to the `listen` / `add_service` call).
45
45
  attr_reader :host_params
46
46
 
47
- # a unique UUID to identify the object - used to make sure Radis broadcasts don't triger the
47
+ # Get's the websocket's unique identifier for unicast transmissions.
48
+ #
49
+ # This UUID is also used to make sure Radis broadcasts don't triger the
48
50
  # boadcasting object's event.
49
- attr_reader :uuid
51
+ def uuid
52
+ @uuid ||= SecureRandom.uuid
53
+ end
54
+ alias :unicast_id :uuid
50
55
 
51
56
  # checks whether this instance accepts broadcasts (WebSocket instances).
52
57
  def accepts_broadcast?
@@ -54,6 +59,8 @@ module Plezi
54
59
  end
55
60
  # sets the controller to refuse "broadcasts".
56
61
  #
62
+ # The controller will also refuse any messages directed at it using the `unicast` method.
63
+ #
57
64
  # This allows some websocket connections to isolate themselves even before they are fully disconnected.
58
65
  #
59
66
  # call this method once it is clear the websocket connection should be terminated.
@@ -253,7 +260,12 @@ module Plezi
253
260
  def broadcast method_name, *args, &block
254
261
  return false unless self.class.public_instance_methods.include?(method_name)
255
262
  @uuid ||= SecureRandom.uuid
256
- self.class.__inner_redis_broadcast(uuid, method_name, args, &block) || self.class.__inner_process_broadcast(uuid, method_name.to_sym, args, &block)
263
+ self.class.__inner_redis_broadcast(uuid, nil, method_name, args, &block) || self.class.__inner_process_broadcast(uuid, nil, method_name.to_sym, args, &block)
264
+ end
265
+
266
+ # {include:ControllerMagic::ClassMethods#unicast}
267
+ def unicast target_uuid, method_name, *args
268
+ self.class.unicast target_uuid, method_name, *args
257
269
  end
258
270
 
259
271
  # WebSockets.
@@ -370,9 +382,9 @@ module Plezi
370
382
  # end
371
383
 
372
384
 
373
- # reviews the Redis connection, sets it up if it's missing and returns the Redis connection.
385
+ # Reviews the Redis connection, sets it up if it's missing and returns the Redis connection.
374
386
  #
375
- # a Redis connection will be automatically created if the `ENV['PL_REDIS_URL']` is set.
387
+ # A Redis connection will be automatically created if the `ENV['PL_REDIS_URL']` is set.
376
388
  # for example:
377
389
  # ENV['PL_REDIS_URL'] = ENV['REDISCLOUD_URL']`
378
390
  # or
@@ -409,7 +421,7 @@ module Plezi
409
421
  on.message do |channel, msg|
410
422
  args = JSON.parse(msg)
411
423
  params = args.shift
412
- __inner_process_broadcast params['_pl_ignore_object'], params['_pl_method_broadcasted'].to_sym, args
424
+ __inner_process_broadcast params['_pl_ignore_object'], params['_pl_target_object'], params['_pl_method_broadcasted'].to_sym, args
413
425
  end
414
426
  end
415
427
  rescue Exception => e
@@ -427,16 +439,16 @@ module Plezi
427
439
  end
428
440
 
429
441
  # broadcasts messages (methods) for this process
430
- def __inner_process_broadcast ignore, method_name, args, &block
431
- ObjectSpace.each_object(self) { |controller| Plezi.callback controller, method_name, *args, &block if controller.accepts_broadcast? && (!ignore || controller.uuid != ignore) }
442
+ def __inner_process_broadcast ignore, target, method_name, args, &block
443
+ ObjectSpace.each_object(self) { |controller| Plezi.callback controller, method_name, *args, &block if controller.accepts_broadcast? && (!ignore || (controller.uuid != ignore)) && (!target || (controller.uuid == target)) }
432
444
  end
433
445
 
434
446
  # broadcasts messages (methods) between all processes (using Redis).
435
- def __inner_redis_broadcast ignore, method_name, args, &block
447
+ def __inner_redis_broadcast ignore, target, method_name, args, &block
436
448
  return false unless redis_connection
437
449
  raise "Radis broadcasts cannot accept blocks (no inter-process callbacks of memory sharing)!" if block
438
450
  # raise "Radis broadcasts accept only one paramater, which is an optional Hash (no inter-process memory sharing)" if args.length > 1 || (args[0] && !args[0].is_a?(Hash))
439
- args.unshift ({_pl_method_broadcasted: method_name, _pl_ignore_object: ignore})
451
+ args.unshift ({_pl_method_broadcasted: method_name, _pl_ignore_object: ignore, _pl_target_object: target})
440
452
  redis_connection.publish(redis_channel_name, args.to_json )
441
453
  true
442
454
  end
@@ -456,7 +468,22 @@ module Plezi
456
468
  # the method will be called asynchrnously for each sibling instance of this Controller class.
457
469
  def broadcast method_name, *args, &block
458
470
  return false unless public_instance_methods.include?(method_name)
459
- __inner_redis_broadcast(nil, method_name, args, &block) || __inner_process_broadcast(nil, method_name.to_sym, args, &block)
471
+ __inner_redis_broadcast(nil, nil, method_name, args, &block) || __inner_process_broadcast(nil, nil, method_name.to_sym, args, &block)
472
+ end
473
+
474
+ # WebSockets.
475
+ #
476
+ # Class method.
477
+ #
478
+ # Use this to unidcast an event to specific websocket connection using it's UUID.
479
+ #
480
+ # accepts:
481
+ # target_uuid:: the target's unique UUID.
482
+ # method_name:: a Symbol with the method's name that should respond to the broadcast.
483
+ # *args:: any arguments that should be passed to the method (IF REDIS IS USED, LIMITATIONS APPLY).
484
+ def unicast target_uuid, method_name, *args
485
+ return false unless public_instance_methods.include?(method_name.to_sym)
486
+ __inner_redis_broadcast(nil, target_uuid, method_name, args) || __inner_process_broadcast(nil, target_uuid, method_name.to_sym, args)
460
487
  end
461
488
 
462
489
  # WebSockets.
@@ -50,6 +50,7 @@ module Plezi
50
50
  def disconnect
51
51
  @response.close if @response
52
52
  end
53
+ alias :close :disconnect
53
54
 
54
55
  # sends data through the socket. a shortcut for ws_client.response <<
55
56
  def << data
@@ -73,10 +73,12 @@ module Plezi
73
73
  self
74
74
  end
75
75
 
76
+ # a closeing Proc
77
+ CLOSE_PROC = Proc.new {|c| c.send "\x88\x00"; c.close}
78
+
76
79
  # sends any pending data and closes the connection.
77
80
  def close
78
- service.send self.class.frame_data('', 8)
79
- service.close
81
+ service.locker.locked? ? (EventMachine.queue [service], CLOSE_PROC) : (CLOSE_PROC.call(service))
80
82
  end
81
83
 
82
84
  FRAME_SIZE_LIMIT = 131_072
data/lib/plezi/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Plezi
2
- VERSION = "0.8.5"
2
+ VERSION = "0.8.6"
3
3
  end
data/resources/Gemfile CHANGED
@@ -21,7 +21,7 @@ gem 'plezi'
21
21
  #
22
22
  # gem 'activesupport', :require => ['active_support', 'active_support/core_ext']
23
23
  ## or:
24
- # gem 'activesupport', :require => ['active_support', active_support/all']
24
+ # gem 'activesupport', :require => ['active_support', 'active_support/all']
25
25
 
26
26
 
27
27
  ####################
data/test/plezi_tests.rb CHANGED
@@ -110,8 +110,18 @@ class TestCtrl
110
110
  #
111
111
  # data is a string that contains binary or UTF8 (message dependent) data.
112
112
  def on_message data
113
- broadcast :_push, data
114
- _push data
113
+ case data
114
+ when 'get uuid'
115
+ response << "uuid: #{uuid}"
116
+ when /to: ([^\s]*)/
117
+ # puts "cating target: #{data.match(/to: ([^\s]*)/)[1]}"
118
+ unicast data.match(/to: ([^\s]*)/)[1], :_push, "unicast"
119
+ # broadcast :_push, "unicast"
120
+ else
121
+ broadcast :_push, data
122
+ _push data
123
+ end
124
+ return true
115
125
  end
116
126
 
117
127
  # called when a disconnect packet has been recieved or the connection has been cut
@@ -129,8 +139,8 @@ module PleziTestTasks
129
139
  module_function
130
140
 
131
141
 
132
- RESULTS = {true => "\e[32mpassed\e[0m"}
133
- RESULTS.default = "\e[31mFAILED!\e[0m"
142
+ RESULTS = {true => "\e[32mpassed\e[0m", :waiting => "\e[32mwaiting validation\e[0m", :failed => "\e[31mFAILED!\e[0m"}
143
+ RESULTS.default = RESULTS[:failed]
134
144
 
135
145
  def run_tests
136
146
  (public_methods(false)).each {|m| method(m).call if m.to_s.match /^test_/}
@@ -221,44 +231,50 @@ module PleziTestTasks
221
231
  puts " **** #url_for test FAILED TO RUN!!!"
222
232
  puts e
223
233
  end
224
- def test_404
225
- puts " * 404 not found and router continuity tests: #{RESULTS[ Net::HTTP.get_response(URI.parse "http://localhost:3000/get404" ).code == '404' ]}"
226
-
227
- rescue => e
228
- puts " **** 404 not found test FAILED TO RUN!!!"
229
- puts e
230
- end
231
- def test_500
232
- workers = Plezi::EventMachine.count_living_workers
233
- puts " * 500 internal error test: #{RESULTS[ Net::HTTP.get_response(URI.parse "http://localhost:3000/fail" ).code == '500' ]}"
234
- # cause 10 more exceptions to be raised... testing thread survival.
235
- 10.times { Net::HTTP.get_response(URI.parse "http://localhost:3000/fail" ).code }
236
- workers_after_test = Plezi::EventMachine.count_living_workers
237
- puts " * Worker survival test: #{RESULTS[workers_after_test == workers]} (#{workers_after_test} out of #{workers})"
238
-
239
- rescue => e
240
- puts " **** 500 internal error test FAILED TO RUN!!!"
241
- puts e
242
- end
243
234
  def test_websocket
244
- connection_test = broadcast_test = echo_test = false
235
+ connection_test = broadcast_test = echo_test = unicast_test = false
245
236
  begin
237
+ ws4 = Plezi::WebsocketClient.connect_to("wss://localhost:3030") do |msg|
238
+ if msg == "unicast"
239
+ puts " * Websocket unicast testing: #{RESULTS[false]}"
240
+ unicast_test = :failed
241
+ end
242
+ end
246
243
  ws2 = Plezi::WebsocketClient.connect_to("wss://localhost:3030") do |msg|
247
- break unless @is_connected || !(@is_connected = true)
248
- puts " * Websocket broadcast message test: #{RESULTS[broadcast_test = (msg == 'echo test')]}"
249
- response.close
250
- go_test = false
244
+ next unless @is_connected || !(@is_connected = true)
245
+ if msg == "unicast"
246
+ puts " * Websocket unicast message test: #{RESULTS[false]}"
247
+ unicast_test = :failed
248
+ next
249
+ else
250
+ puts " * Websocket broadcast message test: #{RESULTS[broadcast_test = (msg == 'echo test')]}"
251
+ go_test = false
252
+ end
251
253
  end
254
+ ws3 = Plezi::WebsocketClient.connect_to("wss://localhost:3030") do |msg|
255
+ if msg.match /uuid: ([^s]*)/
256
+ ws2 << "to: #{msg.match(/^uuid: ([^s]*)/)[1]}"
257
+ puts " * Websocket UUID for unicast testing: #{msg.match(/^uuid: ([^s]*)/)[1]}"
258
+ elsif msg == "unicast"
259
+ puts " * Websocket unicast testing: #{RESULTS[:waiting]}"
260
+ unicast_test ||= true
261
+ end
262
+ end
263
+ ws3 << 'get uuid'
252
264
  puts " * Websocket SSL client test: #{RESULTS[ws2 && true]}"
253
265
  ws1 = Plezi::WebsocketClient.connect_to("ws://localhost:3000") do |msg|
254
266
  unless @connected
255
267
  puts " * Websocket connection message test: #{RESULTS[connection_test = (msg == 'connected')]}"
256
268
  @connected = true
257
269
  response << "echo test"
258
- break
270
+ next
271
+ end
272
+ if msg == "unicast"
273
+ puts " * Websocket unicast testing: #{RESULTS[false]}"
274
+ unicast_test = :failed
275
+ next
259
276
  end
260
277
  puts " * Websocket echo message test: #{RESULTS[echo_test = (msg == 'echo test')]}"
261
- response.close
262
278
  end
263
279
 
264
280
  rescue => e
@@ -267,16 +283,39 @@ module PleziTestTasks
267
283
  end
268
284
  remote = Plezi::WebsocketClient.connect_to("wss://echo.websocket.org/") {|msg| puts " * Extra Websocket Remote test (SSL: echo.websocket.org): #{RESULTS[msg == 'Hello websockets!']}"; response.close}
269
285
  remote << "Hello websockets!"
270
- sleep 0.3
286
+ sleep 0.5
287
+ [ws1, ws2, ws3, ws4, remote].each {|ws| ws.close}
271
288
  PL.on_shutdown {puts " * Websocket connection message test: #{RESULTS[connection_test]}" unless connection_test}
272
289
  PL.on_shutdown {puts " * Websocket echo message test: #{RESULTS[echo_test]}" unless echo_test}
273
290
  PL.on_shutdown {puts " * Websocket broadcast message test: #{RESULTS[broadcast_test]}" unless broadcast_test}
291
+ PL.on_shutdown {puts " * Websocket unicast message test: #{RESULTS[unicast_test]}"}
292
+ end
293
+ def test_404
294
+ puts " * 404 not found and router continuity tests: #{RESULTS[ Net::HTTP.get_response(URI.parse "http://localhost:3000/get404" ).code == '404' ]}"
295
+
296
+ rescue => e
297
+ puts " **** 404 not found test FAILED TO RUN!!!"
298
+ puts e
299
+ end
300
+ def test_500
301
+ workers = Plezi::EventMachine.count_living_workers
302
+ putc " * 500 internal error test: #{RESULTS[ Net::HTTP.get_response(URI.parse "http://localhost:3000/fail" ).code == '500' ]}"
303
+ # cause 10 more exceptions to be raised... testing thread survival.
304
+ 10.times { putc "."; Net::HTTP.get_response(URI.parse "http://localhost:3000/fail" ).code }
305
+ putc "\n"
306
+ workers_after_test = Plezi::EventMachine.count_living_workers
307
+ puts " * Worker survival test: #{RESULTS[workers_after_test == workers]} (#{workers_after_test} out of #{workers})"
308
+
309
+ rescue => e
310
+ puts " **** 500 internal error test FAILED TO RUN!!!"
311
+ puts e
274
312
  end
275
313
  end
276
314
 
277
315
  NO_PLEZI_AUTO_START = true
278
316
 
279
317
  PL.create_logger '/dev/null'
318
+ # PL.max_threads = 4
280
319
 
281
320
  listen port: 3000
282
321
 
@@ -298,12 +337,18 @@ puts " --- Failed tests should read: #{PleziTestTasks::RESULTS[false]}"
298
337
  PleziTestTasks.run_tests
299
338
 
300
339
 
301
- Plezi::EventMachine.clear_timers
340
+ # Plezi::EventMachine.clear_timers
302
341
 
303
342
  sleep PLEZI_TEST_TIME if defined? PLEZI_TEST_TIME
304
343
 
305
344
  Plezi::DSL.stop_services
306
-
345
+ puts "#{Plezi::EventMachine::EM_IO.count} connections awaiting shutdown."
346
+ Plezi::EventMachine.stop_connections
347
+ puts "#{Plezi::EventMachine::EM_IO.count} connections awaiting shutdown after connection close attempt."
348
+ if Plezi::EventMachine::EM_IO.count > 0
349
+ Plezi::EventMachine.forget_connections
350
+ puts "#{Plezi::EventMachine::EM_IO.count} connections awaiting shutdown after connections were forgotten."
351
+ end
307
352
  Plezi::EventMachine.shutdown
308
353
 
309
354
 
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.8.5
4
+ version: 0.8.6
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-06-15 00:00:00.000000000 Z
11
+ date: 2015-06-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler