plezi 0.10.12 → 0.10.13

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: 160e5060bd9cf3f8ec6acf8caa7d672ae238f3c7
4
- data.tar.gz: d2816045210ac66cec18b0c5f37060a9ec5ae271
3
+ metadata.gz: 7f37f01622fbcda6cf86ca9575aafb2145b5479f
4
+ data.tar.gz: cbdf67890ad9995b11d058cbe821c62fad863995
5
5
  SHA512:
6
- metadata.gz: 59691c8ce5b334a0e1b7223aaeb4e0ae74f5f800e24e803e8ad6e8aeb9492f299c71c50c46ca740a8a1940c7c50e4a28f332365ff40853b9221864cb8e3b2769
7
- data.tar.gz: f738389e77c3bdc471e6000db4bfd83f8cbb0133814074c0ba4cbc69c5ecae229a18904f5cd3a8ca32253c7266637563c356cda8ca1cb1f2db4936c09d1a3c15
6
+ metadata.gz: f940d9e1731e9708228c8519a3be967762c427db4d3644aac6ceb439db8a9e44ac1de7bdfd4d3e998039f70452e1e2ffe02dc4ab444c4445793a017c1c02b432
7
+ data.tar.gz: 9a336108299a74946dd97b6718f2440f80df3d5ec6dc33a925c35e5f82eb0d15c21571bbdd6a79c7c1b3cbfafd7ee79a89b3b449773e467018667a21948be1c0
@@ -1,5 +1,23 @@
1
1
  #Change Log
2
2
 
3
+ Change log v.0.10.13
4
+
5
+ **Fix**: The Placebo API was tested and an issue with the new Placebo class broadcast method was fixed.
6
+
7
+ **Update**: Websocket code refactoring unified Placebo and Controller's API and bahavior.
8
+
9
+ **Update**: Unicasting performance using Redis was improved by creating a different Redis channel for each process, so that the unicast is only sent to the process containing the receiver.
10
+
11
+ this should mean that apps using unicasting can scale freely while apps using broadcasting need to address boradcasting considirations (broadcasting causes ALL the websocket connections - in ALL processes - to answer a broadcast, which raises scaling considirations).
12
+
13
+ ***
14
+
15
+ Change log v.0.10.12
16
+
17
+ **Placebo API**: The Placebo API was reviews and revamped slightly, to allow you a better experience and better error feedback.
18
+
19
+ **Template**: The template's error pages were restyled with Plezi's new color scheme.
20
+
3
21
  ***
4
22
 
5
23
  Change log v.0.10.12
data/README.md CHANGED
@@ -4,11 +4,15 @@
4
4
 
5
5
  Plezi is an easy to use Ruby Websocket Framework, with full RESTful routing support and HTTP streaming support. It's name comes from the word "fun", or "pleasure", since Plezi is a pleasure to work with.
6
6
 
7
- I Believe that Plezi is a wonderful backend solution for developing SPAs, both with it's real-time native Websocket API and with it's RESTful routes that work great for writing easy AJAX requests.
7
+ With Plezi, you can easily:
8
8
 
9
- Plezi can both provide a wonderful alternative to existing complex platforms (i.e. Rails/Sinatra/Faye/EM) and augment an existing Rails/Sinatra app, by providing it with easy Websocket and Asynchronous Events support. It's also great as an alternative to socket.io, allowing for both websockets and long pulling.
9
+ 1. Add Websocket services and RESTful HTTP Streaming to your existing Web-App, (Rails/Sinatra or any other Rack based Ruby app).
10
10
 
11
- Plezi runs over the [GRHttp server](https://github.com/boazsegev/GRHttp), which is a pure Ruby HTTP and Websocket Generic Server build using [GReactor](https://github.com/boazsegev/GReactor) - a multi-threaded pure ruby alternative to EventMachine with basic process forking support (enjoy it, if your code is scaling ready).
11
+ 2. Create an easily scalable backend for your SPA.
12
+
13
+ 3. Create a full fledged Ruby web application, taking full advantage of RESTful routing, HTTP streaming and scalable Websocket features.
14
+
15
+ Plezi leverages [GRHttp server](https://github.com/boazsegev/GRHttp)'s new architecture. GRHttp is a pure Ruby HTTP and Websocket Generic Server built using [GReactor](https://github.com/boazsegev/GReactor) - a multi-threaded pure ruby alternative to EventMachine with basic process forking support (enjoy forking, if your code is scaling ready).
12
16
 
13
17
  ## Installation
14
18
 
@@ -43,21 +47,11 @@ the default first port for the app is 3000. you can set the first port to listen
43
47
 
44
48
  you now have a smart framework app that will happily assimilate any gem you feed it. it responds extra well to Haml, Sass and Coffee-Script, which you can enable in it's Gemfile.
45
49
 
46
- ## Barebones Web Service
47
-
48
- This example is basic, useless, but required for every doc out there...
49
-
50
- "Hello World!" in 3 lines - try it in irb (exit irb to start server):
51
-
52
- require 'plezi'
53
- listen
54
- route(/.?/) { |req, res| res << "Hello World!" }
55
-
56
- After you exit irb, the Plezi server will start up. Go to http://localhost:3000/ and see it run :)
57
-
58
50
  ## Plezi Controller classes
59
51
 
60
- One of the best things about the Plezi is it's ability to take in any class as a controller class and route to the classes methods with special support for RESTful methods (`index`, `show`, `new`, `save`, `update`, `delete`, `before` and `after`) and for WebSockets (`pre_connect`, `on_open`, `on_message(data)`, `on_close`, `broadcast`, `unicast`, `on_broadcast(data)`):
52
+ One of the best things about the Plezi is it's ability to take in any class as a controller class and route to the classes methods with special support for RESTful methods (`index`, `show`, `new`, `save`, `update`, `delete`, `before` and `after`) and for WebSockets (`pre_connect`, `on_open`, `on_message(data)`, `on_close`, `broadcast`, `unicast`, `multicast`, `on_broadcast(data)`).
53
+
54
+ Here is a Hello World using a Controller class (run in `irb`):
61
55
 
62
56
  require 'plezi'
63
57
 
@@ -70,11 +64,13 @@ One of the best things about the Plezi is it's ability to take in any class as a
70
64
  listen
71
65
  route '*' , Controller
72
66
 
73
- Except for WebSockets, returning a String will automatically add the string to the response before sending the response - which makes for cleaner code. It's also possible to send the response as it is (by returning true).
67
+ exit # Plezi will autostart once you exit irb.
68
+
69
+ Except while using WebSockets, returning a String will automatically add the string to the response before sending the response - which makes for cleaner code. It's also possible to use the `response` object to set the response or stream HTTP (return true instead of a stream when you're done).
74
70
 
75
- Controllers can even be nested (order matters) or have advanced uses that are definitly worth exploring.
71
+ It's also possible to define a number of controllers for a similar route. The controllers will answer in the order in which the routes are defined (this allows to group code by logic instead of url).
76
72
 
77
- \* please read the demo code for Plezi::StubRESTCtrl and Plezi::StubWSCtrl to learn more. Also, read more about the [GRHttp server](GRHttp websocket and HTTP server) at the core of Plezi to get more information about the amazing [HTTPRequest](http://www.rubydoc.info/gems/grhttp/0.0.6/GRHttp/HTTPRequest) and [HTTPResponse](http://www.rubydoc.info/gems/grhttp/GRHttp/HTTPResponse) objects.
73
+ \* please read the demo code for Plezi::StubRESTCtrl and Plezi::StubWSCtrl to learn more. Also, read more about the [GRHttp Websocket and HTTP server](https://github.com/boazsegev/GRHttp) at the core of Plezi to get more information about the amazing [HTTPRequest](http://www.rubydoc.info/github/boazsegev/GRHttp/master/GRHttp/HTTPRequest) and [HTTPResponse](http://www.rubydoc.info/github/boazsegev/GRHttp/master/GRHttp/HTTPResponse) objects.
78
74
 
79
75
  ## Native Websocket and Redis support
80
76
 
@@ -45,6 +45,7 @@ require 'plezi/helpers/mime_types.rb'
45
45
  ### HTTP and WebSocket Handlers
46
46
  require 'plezi/handlers/http_router.rb'
47
47
  require 'plezi/handlers/route.rb'
48
+ require 'plezi/handlers/ws_object.rb'
48
49
  require 'plezi/handlers/controller_magic.rb'
49
50
  require 'plezi/handlers/controller_core.rb'
50
51
  require 'plezi/handlers/placebo.rb'
@@ -10,9 +10,9 @@ module Plezi
10
10
  # port:: port number. defaults to 3000 or the port specified when the script was called.
11
11
  # host:: the host name. defaults to any host not explicitly defined (a catch-all). NOTICE: in order to allow for hostname aliases, this is host emulation and the listening socket will bind to all the addresses available. To limit the actual binding use the `:bind` parameter as set by the GReactor's API - in which case host aliases might not work.
12
12
  # alias:: a String or an Array of Strings which represent alternative host names (i.e. `alias: ["admin.google.com", "admin.gmail.com"]`).
13
- # root:: the public root folder. if this is defined, static files will be served from the location.
13
+ # public:: the public root folder. if this is defined, static files will be served from this folder and all it's sub-folders. Plezi does NOT support file indexing.
14
14
  # assets:: the assets root folder. defaults to nil (no assets support). if the path is defined, assets will be served from `/assets/...` (or the public_asset path defined) before any static files. assets will not be served if the file in the /public/assets folder if up to date (a rendering attempt will be made for systems that allow file writing).
15
- # assets_public:: the assets public uri location (uri format, NOT a file path). defaults to `/assets`. assets will be saved (or rendered) to the assets public folder and served as static files.
15
+ # assets_public:: the assets public uri location (uri format, NOT a file path). defaults to `/assets`. `save_assets` will set if assets should be saved to the assets public folder as static files (defaults to false).
16
16
  # assets_callback:: a method that accepts two parameters: (request, response) and renders any custom assets. the method should return `false` unless it had set the response.
17
17
  # save_assets:: saves the rendered assets to the filesystem, under the public folder. defaults to false.
18
18
  # templates:: the templates root folder. defaults to nil (no template support). templates can be rendered by a Controller class, using the `render` method.
@@ -20,14 +20,16 @@ module Plezi
20
20
  # ssl_key:: the public key for the SSL service.
21
21
  # ssl_cert:: the certificate for the SSL service.
22
22
  #
23
- # assets:
23
+ # Assets:
24
24
  #
25
- # assets support will render `.sass`, `.scss` and `.coffee` and save them as local files (`.css`, `.css`, and `.js` respectively)
25
+ # Assets support will render `.sass`, `.scss` and `.coffee` and save them as local files (`.css`, `.css`, and `.js` respectively)
26
26
  # before sending them as static files.
27
27
  #
28
+ # Should you need to render a different type of asset, you can define an assets_callback (or submit a pull request with a patch).
29
+ #
28
30
  # templates:
29
31
  #
30
- # ERB, Slim and Haml are natively supported. Otherwise define an assets_callback (or submit a pull request with a patch).
32
+ # Plezi's controller.render ERB, Slim and Haml are natively supported.
31
33
  #
32
34
  # @returns [Plezi::Router]
33
35
  #
@@ -36,6 +38,8 @@ module Plezi
36
38
  parameters[:index_file] ||= 'index.html'
37
39
  parameters[:assets_public] ||= '/assets'
38
40
  parameters[:assets_public].chomp! '/'
41
+ parameters[:public] ||= parameters[:root] # backwards compatability
42
+ puts "Warning: 'root' option is being depracated. use 'public' instead." if parameters[:root]
39
43
 
40
44
  # check if the port is used twice.
41
45
  @routers_locker.synchronize do
@@ -21,7 +21,7 @@ module Plezi
21
21
  raise "Redis connction failed for: #{ENV['PL_REDIS_URL']}" unless @redis
22
22
  @redis_sub_thread = Thread.new do
23
23
  begin
24
- Redis.new(host: @redis_uri.host, port: @redis_uri.port, password: @redis_uri.password).subscribe(Plezi::Settings.redis_channel_name) do |on|
24
+ Redis.new(host: @redis_uri.host, port: @redis_uri.port, password: @redis_uri.password).subscribe(Plezi::Settings.redis_channel_name, Plezi::Settings.uuid) do |on|
25
25
  on.message do |channel, msg|
26
26
  begin
27
27
  data = YAML.load(msg)
@@ -51,7 +51,7 @@ module Plezi
51
51
  GRHttp.ws_message_size_limit
52
52
  end
53
53
 
54
- # This Server's UUID
54
+ # This Server's UUID, for Redis and unicasting identification.
55
55
  def uuid
56
56
  @uuid ||= SecureRandom.uuid
57
57
  end
@@ -4,6 +4,7 @@ module Plezi
4
4
  # the methods defined in this module will be injected into the Controller's Core class (inherited from the controller).
5
5
  module ControllerCore
6
6
  def self.included base
7
+ base.send :include, Plezi::Base::WSObject
7
8
  base.send :include, InstanceMethods
8
9
  base.extend ClassMethods
9
10
  end
@@ -65,7 +66,7 @@ module Plezi
65
66
  return false
66
67
  end
67
68
  return false if data[:type] && data[:type] != :all && !self.is_a?(data[:type])
68
- return false if data[:target] && data[:target] != ws.uuid
69
+ # return false if data[:target] && data[:target] != ws.uuid + Plezi::Settings.uuid # already reviewed by the GRHttp
69
70
  return false unless self.class.has_method?(data[:method])
70
71
  self.method(data[:method]).call *data[:data]
71
72
  end
@@ -90,43 +91,6 @@ module Plezi
90
91
  end
91
92
 
92
93
  module ClassMethods
93
- public
94
-
95
- def reset_routing_cache
96
- @methods_list = nil
97
- @exposed_methods_list = nil
98
- @super_methods_list = nil
99
- has_method? nil
100
- has_exposed_method? nil
101
- has_super_method? nil
102
- end
103
- def has_method? method_name
104
- @methods_list ||= self.instance_methods.to_set
105
- @methods_list.include? method_name
106
- end
107
- def has_exposed_method? method_name
108
- @exposed_methods_list ||= ( (self.public_instance_methods - Class.new.instance_methods - Plezi::ControllerMagic::InstanceMethods.instance_methods - [:before, :after, :save, :show, :update, :delete, :initialize, :on_message, :on_broadcast, :pre_connect, :on_open, :on_close]).delete_if {|m| m.to_s[0] == '_'} ).to_set
109
- @exposed_methods_list.include? method_name
110
- end
111
- def has_super_method? method_name
112
- @super_methods_list ||= self.superclass.instance_methods.to_set
113
- @super_methods_list.include? method_name
114
- end
115
-
116
- protected
117
-
118
- # a callback that resets the class router whenever a method (a potential route) is added
119
- def method_added(id)
120
- reset_routing_cache
121
- end
122
- # a callback that resets the class router whenever a method (a potential route) is removed
123
- def method_removed(id)
124
- reset_routing_cache
125
- end
126
- # a callback that resets the class router whenever a method (a potential route) is undefined (using #undef_method).
127
- def method_undefined(id)
128
- reset_routing_cache
129
- end
130
94
  end
131
95
  end
132
96
  end
@@ -204,106 +204,11 @@ module Plezi
204
204
  end
205
205
  false
206
206
  end
207
-
208
- ## WebSockets Magic
209
-
210
- # Get's the websocket's unique identifier for unicast transmissions.
211
- #
212
- # This UUID is also used to make sure Radis broadcasts don't triger the
213
- # boadcasting object's event.
214
- def uuid
215
- return @response.uuid if @response.is_a?(GRHttp::WSEvent)
216
- nil
217
- end
218
- alias :unicast_id :uuid
219
-
220
- # WebSockets.
221
- #
222
- # Use this to brodcast an event to all 'sibling' websockets (websockets that have been created using the same Controller class).
223
- #
224
- # Accepts:
225
- # method_name:: a Symbol with the method's name that should respond to the broadcast.
226
- # args*:: The method's argumenst - It MUST be possible to stringify the arguments into a YAML string, or broadcasting and unicasting will fail when scaling beyond one process / one machine.
227
- #
228
- # The method will be called asynchrnously for each sibling instance of this Controller class.
229
- #
230
- def broadcast method_name, *args
231
- return false unless self.class.has_method? method_name
232
- self.class._inner_broadcast({ method: method_name, data: args, type: self.class}, @response.io)
233
- end
234
-
235
- # {include:ControllerMagic::ClassMethods#unicast}
236
- def unicast target_uuid, method_name, *args
237
- self.class._inner_broadcast method: method_name, data: args, target: target_uuid
238
- end
239
-
240
- # Use this to multicast an event to ALL websocket connections on EVERY controller, including Placebo controllers.
241
- #
242
- # Accepts:
243
- # method_name:: a Symbol with the method's name that should respond to the broadcast.
244
- # args*:: The method's argumenst - It MUST be possible to stringify the arguments into a YAML string, or broadcasting and unicasting will fail when scaling beyond one process / one machine.
245
- #
246
- # The method will be called asynchrnously for ALL websocket connections.
247
- #
248
- def multicast method_name, *args
249
- self.class._inner_broadcast({ method: method_name, data: args, type: :all}, @response.io)
250
- end
251
-
252
-
253
- # # will (probably NOT), in the future, require authentication or, alternatively, return an Array [user_name, password]
254
- # #
255
- # #
256
- # def request_http_auth realm = false, auth = 'Digest'
257
- # return request.service.handler.hosts[request[:host] || :default].send_by_code request, 401, "WWW-Authenticate" => "#{auth}#{realm ? "realm=\"#{realm}\"" : ''}" unless request['authorization']
258
- # request['authorization']
259
- # end
260
207
  end
261
208
 
262
209
  module ClassMethods
263
210
  public
264
211
 
265
- # WebSockets: fires an event on all of this controller's active websocket connections.
266
- #
267
- # Class method.
268
- #
269
- # Use this to brodcast an event to all connections.
270
- #
271
- # accepts:
272
- # method_name:: a Symbol with the method's name that should respond to the broadcast.
273
- # *args:: any arguments that should be passed to the method (IF REDIS IS USED, LIMITATIONS APPLY).
274
- #
275
- # this method accepts and optional block (NON-REDIS ONLY) to be used as a callback for each sibling's event.
276
- #
277
- # the method will be called asynchrnously for each sibling instance of this Controller class.
278
- def broadcast method_name, *args
279
- return false unless has_method? method_name
280
- _inner_broadcast method: method_name, data: args, type: self
281
- end
282
-
283
- # WebSockets: fires an event on a specific websocket connection using it's UUID.
284
- #
285
- # Use this to unidcast an event to specific websocket connection using it's UUID.
286
- #
287
- # accepts:
288
- # target_uuid:: the target's unique UUID.
289
- # method_name:: a Symbol with the method's name that should respond to the broadcast.
290
- # *args:: any arguments that should be passed to the method (IF REDIS IS USED, LIMITATIONS APPLY).
291
- def unicast target_uuid, method_name, *args
292
- _inner_broadcast method: method_name, data: args, target: target_uuid
293
- end
294
-
295
- # Use this to multicast an event to ALL websocket connections on EVERY controller, including Placebo controllers.
296
- #
297
- # Accepts:
298
- # method_name:: a Symbol with the method's name that should respond to the broadcast.
299
- # args*:: The method's argumenst - It MUST be possible to stringify the arguments into a YAML string, or broadcasting and unicasting will fail when scaling beyond one process / one machine.
300
- #
301
- # The method will be called asynchrnously for ALL websocket connections.
302
- #
303
- def multicast method_name, *args
304
- _inner_broadcast({ method: method_name, data: args, type: :all}, @response.io)
305
- end
306
-
307
212
  # This class method behaves the same way as the instance method #url_for. See the instance method's documentation for more details.
308
213
  def url_for dest
309
214
  get_pl_route.url_for dest
@@ -311,7 +216,7 @@ module Plezi
311
216
 
312
217
  # resets the routing cache
313
218
  def reset_routing_cache
314
- @inheritance.each {|sub| sub.reset_routing_cache}
219
+ @inheritance.each {|sub| sub.reset_routing_cache} if @inheritance
315
220
  end
316
221
 
317
222
  protected
@@ -346,29 +251,6 @@ module Plezi
346
251
  def inherited sub
347
252
  (@inheritance ||= [].to_set) << sub
348
253
  end
349
-
350
- public
351
-
352
-
353
- # WebSockets
354
-
355
- # sends the broadcast
356
- def _inner_broadcast data, ignore_io = nil
357
- if data[:target]
358
- __inner_redis_broadcast data unless GRHttp::Base::WSHandler.unicast data[:target], data
359
- else
360
- GRHttp::Base::WSHandler.broadcast data, ignore_io
361
- __inner_redis_broadcast data
362
- end
363
- true
364
- end
365
-
366
- def __inner_redis_broadcast data
367
- conn = Plezi.redis_connection
368
- data[:server] = Plezi::Settings.uuid
369
- return conn.publish( Plezi::Settings.redis_channel_name, data.to_yaml ) if conn
370
- false
371
- end
372
254
  end
373
255
 
374
256
  end
@@ -0,0 +1,161 @@
1
+ module Plezi
2
+
3
+ # This API wil allows you to listen to Websocket Broadcasts sent to any object and to accept unicasts
4
+ # even when NOT connected to a websocket.
5
+ #
6
+ # Simpley create a class to handle any events and call `Plezi::Placebo.new ClassName` :
7
+ #
8
+ # class MyListener
9
+ # def _my_method_name *args
10
+ # #logic
11
+ # end
12
+ # end
13
+ #
14
+ # Plezi::Placebo.new MyListener
15
+ #
16
+ # A new instance will be created and that instance will answer any broadcasts, for ALL possible
17
+ # Plezi controllers, as long as it had a method defined that is capable to handle the broadcast.
18
+ #
19
+ # The new instance will also accept unicasts sent to it's unique UUID.
20
+ #
21
+ # Returns an instance that is a member of the class passed, after that class was inherited by Plezi and
22
+ # more methods were injected into it's subclass.
23
+ module Placebo
24
+
25
+ # the base module exposes some of the core functionality, but shouldn't be relied upon as far as it's API goes.
26
+ module Base
27
+ #the methods here will be injected to the Placebo controller.
28
+ module Core
29
+ def self.included base
30
+ base.send :include, InstanceMethods
31
+ base.extend ClassMethods
32
+ base.superclass.instance_eval {extend SuperClassMethods}
33
+ end
34
+
35
+ #the methods here will be injected to the Placebo controller as Instance methods.
36
+ module InstanceMethods
37
+ public
38
+ attr_accessor :io
39
+ def initialize io
40
+ @io = io
41
+ @io[:websocket_handler] = self
42
+ super()
43
+ end
44
+ # notice of disconnect
45
+ def on_close
46
+ return super() if defined? super
47
+ GR.warn "Placebo #{self.class.superclass.name} disconnected. Ignore if this message appears during shutdown."
48
+ end
49
+
50
+ # handles broadcasts / unicasts
51
+ def on_broadcast ws
52
+ data = ws.data
53
+ unless (data[:type] || data[:target]) && data[:method] && data[:data]
54
+ GReactor.warn "Broadcast message unknown... falling back on base broadcasting"
55
+ return super(data) if defined? super
56
+ return false
57
+ end
58
+ return false if data[:type] && data[:type] != :all && !self.is_a?(data[:type])
59
+ return ((data[:type] == :all) ? false : (raise "Placebo Broadcasting recieved but no method can handle it - dump:\r\n #{data.to_s}") ) unless self.class.has_super_method?(data[:method])
60
+ self.method(data[:method]).call *data[:data]
61
+ end
62
+ # Returns the websocket connection's UUID, used for unicasting.
63
+ def uuid
64
+ return (@uuid ||= @response.uuid + Plezi::Settings.uuid) if @response.is_a?(GRHttp::WSEvent)
65
+ end
66
+
67
+ # Performs a websocket unicast to the specified target.
68
+ def unicast target_uuid, method_name, *args
69
+ self.class.unicast target_uuid, method_name, *args
70
+ end
71
+ # broadcast to a specific controller
72
+ def broadcast controller_class, method_name, *args
73
+ GRHttp::Base::WSHandler.broadcast({data: args, type: controller_class, method: method_name}, self)
74
+ self.class.__send_to_redis data: args, type: controller_class, method: method_name
75
+ end
76
+ # multicast to all handlers.
77
+ def multicast method_name, *args
78
+ self.class.multicast method_name, *args
79
+ end
80
+ end
81
+ #the methods here will be injected to the Placebo controller as class methods.
82
+ module ClassMethods
83
+ public
84
+ def has_super_method? method_name
85
+ @super_methods_list ||= self.superclass.instance_methods.to_set
86
+ @super_methods_list.include? method_name
87
+ end
88
+ end
89
+ module SuperClassMethods
90
+ # multicast to all handlers.
91
+ def multicast method_name, *args
92
+ GRHttp::Base::WSHandler.broadcast({method: method_name, data: args, type: :all}, self)
93
+ __send_to_redis method: method_name, data: args, type: :all
94
+ end
95
+ # Broadcast to all instances (usually one instance) of THIS placebo controller.
96
+ #
97
+ # This should be used by the real websocket connections to forward messages to the placebo controller classes.
98
+ def broadcast method_name, *args
99
+ @methods_list ||= self.public_instance_methods.to_set
100
+ raise "No mothod defined to accept this broadcast." unless @methods_list.include? method_name
101
+ GRHttp::Base::WSHandler.broadcast data: args, type: self, method: method_name
102
+ __send_to_redis data: args, type: self, method: method_name
103
+ end
104
+ # Performs a websocket unicast to the specified target.
105
+ def unicast target_uuid, method_name, *args
106
+ GRHttp::Base::WSHandler.unicast target_uuid, data: args, target: target_uuid, method: method_name
107
+ __send_to_redis data: args, target: target_uuid, method: method_name
108
+ end
109
+ protected
110
+ def __send_to_redis data
111
+ raise "Wrong method name for websocket broadcasting - expecting type Symbol" unless data[:method].is_a?(Symbol) || data[:method].is_a?(Symbol)
112
+ conn = Plezi.redis_connection
113
+ data[:server] = Plezi::Settings.uuid
114
+ return conn.publish( Plezi::Settings.redis_channel_name, data.to_yaml ) if conn
115
+ false
116
+ end
117
+ end
118
+ end
119
+ class PlaceboIO < GReactor::BasicIO
120
+ def clear?
121
+ io.closed?
122
+ end
123
+ def call
124
+ self.read
125
+ GR.warn "Placebo IO recieved IO signal - this is unexpected..."
126
+ end
127
+ def on_disconnect
128
+ @params[:out].close rescue nil
129
+ @cache[:websocket_handler].on_close if @cache[:websocket_handler]
130
+ end
131
+ end
132
+ end
133
+ module_function
134
+ def new placebo_class
135
+ new_class_name = "PlaceboPlezi__#{placebo_class.name.gsub /[\:\-\#\<\>\{\}\(\)\s]/, '_'}"
136
+ new_class = nil
137
+ new_class = Module.const_get new_class_name if Module.const_defined? new_class_name
138
+ unless new_class
139
+ new_class = Class.new(placebo_class) do
140
+ include Placebo::Base::Core
141
+ end
142
+ Object.const_set(new_class_name, new_class)
143
+ end
144
+ i, o = IO.pipe
145
+ io = Placebo::Base::PlaceboIO.new i, out: o
146
+ io = GReactor.add_raw_io i, io
147
+ new_class.new(io)
148
+ end
149
+ end
150
+ end
151
+
152
+
153
+ # class A
154
+ # def _hi
155
+ # 'hi'
156
+ # end
157
+ # end
158
+ # Plezi::Placebo.new A
159
+ # a = nil
160
+ # GReactor.each {|h| a= h}
161
+ # a[:websocket_handler].on_broadcast GRHttp::WSEvent.new(nil, type: true, data: [], method: :_hi)