plezi 0.8.5 → 0.8.6

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