plezi 0.10.12 → 0.10.13

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