plezi 0.10.6 → 0.10.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +119 -1
- data/lib/plezi.rb +1 -0
- data/lib/plezi/common/dsl.rb +21 -4
- data/lib/plezi/common/redis.rb +23 -19
- data/lib/plezi/handlers/controller_core.rb +3 -2
- data/lib/plezi/handlers/controller_magic.rb +24 -0
- data/lib/plezi/handlers/http_router.rb +14 -0
- data/lib/plezi/handlers/placebo.rb +123 -0
- data/lib/plezi/handlers/stubs.rb +1 -1
- data/lib/plezi/version.rb +1 -1
- data/plezi.gemspec +1 -1
- data/test/plezi_tests.rb +52 -8
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c504b33f40bbc1d8235c5d48101abab00a0facac
|
4
|
+
data.tar.gz: 5fad18c3d52af38f9ecc94cb899c337b54a3afdd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4f6a25bfa2846fd8ea384b741ee754673c3ecc6384402b9bd8c0766dea71ab0cc9711723ccaf38f20934fbf0b0f05c3c409850aa427a9d87f26775467b7382e
|
7
|
+
data.tar.gz: db2844de88d6dae7a1e019b4ec46fde5f33f4342484e38d5dc03f9aa9e5cc70c897ace96b4fed46f92bfd7e8c4d572f2e4bdc4952caa7dc0b283d93a01c1aaea
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,28 @@
|
|
2
2
|
|
3
3
|
***
|
4
4
|
|
5
|
+
Change log v.0.10.8
|
6
|
+
|
7
|
+
**Fix**: Fixed an issue with the new websocket upgrade handler. It is unclear how come the issue did not show up during the testing.
|
8
|
+
|
9
|
+
\* (All the changes in version 0.10.7 still apply)
|
10
|
+
|
11
|
+
***
|
12
|
+
|
13
|
+
Change log v.0.10.7 (yanked)
|
14
|
+
|
15
|
+
**Fix**: Forces the use of a better version of the GRHttp server, now as fully tested as I could manage. This fixes an issue where the lasy byte on a Websocket message might have been corrupt.
|
16
|
+
|
17
|
+
**Fix**: fixed an issue where websocket connections would be quietly established (messages would be ignored) even though they should have been declined.
|
18
|
+
|
19
|
+
**Update**: Better support for intigration of Plezi with other frameworks, using `Plezi.start_async` and `Plezi.start_placebo` to get all the benifits of Plezi without distrupting the host framework.
|
20
|
+
|
21
|
+
**Update**: added the multicasting feature - allows you to send a message to ALL the websocket connections that defined a method to handle the message - use `multicast :method_name, arg1, arg2, arg3...`.
|
22
|
+
|
23
|
+
**Update**: Added the Placebo API to support websocket broadcasting on normal classes - allows for super-easy framework integration with other Ruby frameworks such as Rails and Sinatra.
|
24
|
+
|
25
|
+
***
|
26
|
+
|
5
27
|
Change log v.0.10.6
|
6
28
|
|
7
29
|
**Performance Boost**: updated the GRHttp server version, to leverage the new Websocket engine, which offers a significant performance boost and allows for larger data to be transmitted over the websocket connection (tested with more than 250MB of data).
|
data/README.md
CHANGED
@@ -10,7 +10,7 @@ Find more info on [Plezi's framework documentation](http://www.rubydoc.info/gems
|
|
10
10
|
|
11
11
|
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" in Haitian, since Plezi is really fun to work with and it keeps our code clean and streamlined.
|
12
12
|
|
13
|
-
Plezi
|
13
|
+
Plezi can both augment an existing Rails/Sinatra app, by providing it with easy Websocket and Asynchronous Events support, as well as offer an alternative to a Rack/Rails/Sintra/Faye/EM-Websockets combo. It's also great as an alternative to socket.io, allowing for both websockets and long pulling.
|
14
14
|
|
15
15
|
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).
|
16
16
|
|
@@ -125,6 +125,124 @@ Remember to connect to the service from at least two browser windows - to truly
|
|
125
125
|
|
126
126
|
method names starting with an underscore ('_') will NOT be made public by the router: so while both '/hello' and '/humans.txt' are public ( [try it](http://localhost:3000/humans.txt) ), '/_send_message' will return a 404 not found error ( [try it](http://localhost:3000/_send_message) ).
|
127
127
|
|
128
|
+
## Augmenting a Rails/Sinatra with Websocket broadcasting
|
129
|
+
|
130
|
+
You already have an amazing WebApp, but now you want to add websocket broadcasting and unicasting support - Plezi makes connection your existing WebApp with your Plezi Websocket backend as easy as it gets.
|
131
|
+
|
132
|
+
Simply include the Plezi App in your existing app and call `Plezi.start_placebo` - now you can access all the websocket API that you want from your existing WebApp.
|
133
|
+
|
134
|
+
For instance, add the following code to your environment on a Rails or Sinatra app:
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
|
138
|
+
require './my_plezi_app/environment.rb'
|
139
|
+
require './my_plezi_app/routes.rb'
|
140
|
+
|
141
|
+
ENV['PL_REDIS_URL'] = "redis://username:password@my.host:6379"
|
142
|
+
|
143
|
+
Plezi.start_placebo
|
144
|
+
```
|
145
|
+
|
146
|
+
That's it!
|
147
|
+
|
148
|
+
Plezi will automatically set up the Redis connections and pub/sub to connect your existing WebApp with Plezi's Websocket backend - which you can safely scale over processes or machines.
|
149
|
+
|
150
|
+
Now you can use Plezi from withing your existing App's code. For example, if your Plezi app has a controller named `ClientPleziCtrl`, you might use:
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
|
154
|
+
class ClientsController < ApplicationController
|
155
|
+
def update
|
156
|
+
#... your original logic here
|
157
|
+
@client = Client.find(params[:id])
|
158
|
+
|
159
|
+
# now unicast data to your client on the websocket
|
160
|
+
# (assume his websocket uuid was saved in @client.ws_uuid)
|
161
|
+
|
162
|
+
ClientPleziCtrl.unicast @client.ws_uuid, :method_name, @client.attributes
|
163
|
+
|
164
|
+
# or broadcast data to your all your the clients currently connected
|
165
|
+
|
166
|
+
ClientPleziCtrl.broadcast :method_name, @client.attributes
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
170
|
+
```
|
171
|
+
|
172
|
+
Easy.
|
173
|
+
|
174
|
+
\- "But wait...", you might say to me, "How do we get information back FROM the back end?"
|
175
|
+
|
176
|
+
Oh, that's easy too.
|
177
|
+
|
178
|
+
With a few more lines of code, we can have the websocket connections _broadcast_ back to us using the `Plezi::Placebo` API.
|
179
|
+
|
180
|
+
On your Rails app, add:
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
|
184
|
+
class MyReciever
|
185
|
+
def my_reciever_method arg1, arg2, arg3, arg4...
|
186
|
+
# your app's logic
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
Plezi::Placebo.new MyReciever
|
191
|
+
|
192
|
+
```
|
193
|
+
|
194
|
+
Plezi will now take your class and add mimick an IO connection (the Placebo connection) on it's GRHttp serever. This Placebo connection will answer the Redis broadcasts just as if your class was a websocket controller...
|
195
|
+
|
196
|
+
On the Plezi side, use multicasting, from ANY controller:
|
197
|
+
|
198
|
+
```ruby
|
199
|
+
|
200
|
+
class ClientPleziCtrl
|
201
|
+
def on_message data
|
202
|
+
# app logic here
|
203
|
+
multicast :my_reciever_method, arg1, arg2, arg3, arg4...
|
204
|
+
end
|
205
|
+
end
|
206
|
+
```
|
207
|
+
|
208
|
+
That's it! Now you have your listening object... but careful - to saafely scale up this communication you might consider using unicasting instead of broadcasting...
|
209
|
+
|
210
|
+
On your Rails app, add:
|
211
|
+
|
212
|
+
```ruby
|
213
|
+
#...
|
214
|
+
class MyReciever
|
215
|
+
def get_controller sender
|
216
|
+
ClientPleziCtrl.unicast sender, :_set_controller_uuid, uuid
|
217
|
+
end
|
218
|
+
def my_reciever_method arg1, arg2, arg3, arg4...
|
219
|
+
# ...
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
Plezi::Placebo.new MyReciever
|
224
|
+
|
225
|
+
```
|
226
|
+
On the Plezi, save the data and use unicasting when possible:
|
227
|
+
|
228
|
+
```ruby
|
229
|
+
class ClientPleziCtrl
|
230
|
+
def on_open
|
231
|
+
multicast :get_controller, uuid
|
232
|
+
end
|
233
|
+
def on_message data
|
234
|
+
# app logic here
|
235
|
+
unicast @main_controller, :my_reciever_method, arg1, arg2, arg3, arg4... if @main_controller
|
236
|
+
end
|
237
|
+
def _controller_uuid controller_uuid
|
238
|
+
@main_controller = controller_uuid
|
239
|
+
# send ready flag to client using JSON?
|
240
|
+
response << "{\"state\":\"ready\""
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
```
|
245
|
+
|
128
246
|
## Native HTTP streaming with Asynchronous events
|
129
247
|
|
130
248
|
Plezi comes with native HTTP streaming support, alowing you to use Plezi Events and Timers to send an Asynchronous response.
|
data/lib/plezi.rb
CHANGED
data/lib/plezi/common/dsl.rb
CHANGED
@@ -72,7 +72,8 @@ module Plezi
|
|
72
72
|
@listeners[parameters[:port]] = parameters
|
73
73
|
|
74
74
|
# make sure the protocol exists.
|
75
|
-
parameters[:
|
75
|
+
parameters[:http_handler] = HTTPRouter.new
|
76
|
+
parameters[:upgrade_handler] = parameters[:http_handler].upgrade_proc
|
76
77
|
|
77
78
|
GRHttp.listen parameters
|
78
79
|
# set the active router to the handler or the protocol.
|
@@ -126,14 +127,30 @@ module Plezi
|
|
126
127
|
end
|
127
128
|
end
|
128
129
|
|
130
|
+
# starts the Plezi framework server and hangs until the exit signal is given.
|
129
131
|
def self.start
|
130
|
-
|
132
|
+
start_async
|
133
|
+
puts "\nPress ^C to exit.\n"
|
134
|
+
GReactor.join { puts "\r\nStarting shutdown sequesnce. Press ^C to force quit."}
|
135
|
+
end
|
136
|
+
# starts the Plezi framework and returns immidiately,
|
137
|
+
# allowing you to run the Plezi framework along side another framework.
|
138
|
+
def self.start_async
|
131
139
|
Object.const_set("NO_PLEZI_AUTO_START", true) unless defined?(NO_PLEZI_AUTO_START)
|
140
|
+
return GReactor.start if GReactor.running?
|
132
141
|
puts "Starting Plezi #{Plezi::VERSION} Services using the GRHttp #{GRHttp::VERSION} server."
|
133
|
-
puts "Press ^C to exit."
|
134
142
|
GReactor.on_shutdown { puts "Plezi shutdown. It was fun to serve you." }
|
135
143
|
GReactor.start Plezi::Settings.max_threads
|
136
|
-
|
144
|
+
end
|
145
|
+
# This allows you to run the Plezi framework along side another framework - WITHOUT running the actual server.
|
146
|
+
#
|
147
|
+
# The server will not be initiatet and instead you will be able to use Plezi controllers and the Redis auto-config
|
148
|
+
# to broadcast Plezi messages to other Plezi processes - allowing for scalable intigration of Plezi into other frameworks.
|
149
|
+
def self.start_placebo
|
150
|
+
GReactor.clear_listeners
|
151
|
+
redis_connection # make sure the redis connection is activated
|
152
|
+
puts "* Plezi #{Plezi::VERSION} Services will start with no Server...\n"
|
153
|
+
start_async
|
137
154
|
end
|
138
155
|
end
|
139
156
|
|
data/lib/plezi/common/redis.rb
CHANGED
@@ -13,29 +13,33 @@ module Plezi
|
|
13
13
|
def redis_connection
|
14
14
|
return @redis if (@redis_sub_thread && @redis_sub_thread.alive?) && @redis
|
15
15
|
return false unless defined?(Redis) && ENV['PL_REDIS_URL']
|
16
|
-
@
|
17
|
-
@
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
Redis
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
16
|
+
@redis_locker ||= Mutex.new
|
17
|
+
@redis_locker.synchronize do
|
18
|
+
return @redis if (@redis_sub_thread && @redis_sub_thread.alive?) && @redis # repeat the test once syncing is done.
|
19
|
+
@redis_uri ||= URI.parse(ENV['PL_REDIS_URL'])
|
20
|
+
@redis ||= Redis.new(host: @redis_uri.host, port: @redis_uri.port, password: @redis_uri.password)
|
21
|
+
raise "Redis connction failed for: #{ENV['PL_REDIS_URL']}" unless @redis
|
22
|
+
@redis_sub_thread = Thread.new do
|
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|
|
25
|
+
on.message do |channel, msg|
|
26
|
+
begin
|
27
|
+
data = YAML.load(msg)
|
28
|
+
next if data[:server] == Plezi::Settings.uuid
|
29
|
+
if data[:target]
|
30
|
+
GRHttp::Base::WSHandler.unicast data[:target], data
|
31
|
+
else
|
32
|
+
GRHttp::Base::WSHandler.broadcast data
|
33
|
+
end
|
34
|
+
rescue => e
|
35
|
+
Reactor.error e
|
30
36
|
end
|
31
|
-
rescue => e
|
32
|
-
Reactor.error e
|
33
37
|
end
|
34
38
|
end
|
39
|
+
rescue => e
|
40
|
+
Reactor.error e
|
41
|
+
retry
|
35
42
|
end
|
36
|
-
rescue => e
|
37
|
-
Reactor.error e
|
38
|
-
retry
|
39
43
|
end
|
40
44
|
end
|
41
45
|
@redis
|
@@ -56,7 +56,7 @@ module Plezi
|
|
56
56
|
def on_close ws
|
57
57
|
super() if defined? super
|
58
58
|
end
|
59
|
-
# handles websocket
|
59
|
+
# handles websocket multicasting/broadcasting/unicasting.
|
60
60
|
def on_broadcast ws
|
61
61
|
data = ws.data
|
62
62
|
unless (data[:type] || data[:target]) && data[:method] && data[:data]
|
@@ -64,7 +64,8 @@ module Plezi
|
|
64
64
|
return super(data) if defined? super
|
65
65
|
return false
|
66
66
|
end
|
67
|
-
return false if data[:type] && !self.is_a?(data[:type])
|
67
|
+
return false if data[:type] && data[:type] != :all && !self.is_a?(data[:type])
|
68
|
+
return false if data[:target] && data[:target] != ws.uuid
|
68
69
|
return false unless self.class.has_method?(data[:method])
|
69
70
|
self.method(data[:method]).call *data[:data]
|
70
71
|
end
|
@@ -237,6 +237,18 @@ module Plezi
|
|
237
237
|
self.class._inner_broadcast method: method_name, data: args, target: target_uuid
|
238
238
|
end
|
239
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
|
+
|
240
252
|
|
241
253
|
# # will (probably NOT), in the future, require authentication or, alternatively, return an Array [user_name, password]
|
242
254
|
# #
|
@@ -280,6 +292,18 @@ module Plezi
|
|
280
292
|
_inner_broadcast method: method_name, data: args, target: target_uuid
|
281
293
|
end
|
282
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
|
+
|
283
307
|
# This class method behaves the same way as the instance method #url_for. See the instance method's documentation for more details.
|
284
308
|
def url_for dest
|
285
309
|
get_pl_route.url_for dest
|
@@ -15,6 +15,20 @@ module Plezi
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
# return the upgrade handler (the self.on_upgrade method)
|
19
|
+
def upgrade_proc
|
20
|
+
self.method :on_upgrade
|
21
|
+
end
|
22
|
+
#handles websocket connection requests.
|
23
|
+
def on_upgrade request, response
|
24
|
+
host = get_host(request[:host_name].to_s.downcase) || @hosts[:default]
|
25
|
+
return false unless host
|
26
|
+
request.io[:params] = host.params
|
27
|
+
# return if a route answered the request
|
28
|
+
host.routes.each {|r| a = r.on_request(request, response); return a if a}
|
29
|
+
# websockets should cut out here
|
30
|
+
false
|
31
|
+
end
|
18
32
|
# initializes an HTTP router (the normal Handler for HTTP requests)
|
19
33
|
#
|
20
34
|
# the router holds the different hosts and sends them messages/requests.
|
@@ -0,0 +1,123 @@
|
|
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
|
+
end
|
33
|
+
|
34
|
+
#the methods here will be injected to the Placebo controller as Instance methods.
|
35
|
+
module InstanceMethods
|
36
|
+
public
|
37
|
+
attr_accessor :io
|
38
|
+
def initialize io
|
39
|
+
@io = io
|
40
|
+
@io[:websocket_handler] = self
|
41
|
+
super()
|
42
|
+
end
|
43
|
+
# handles broadcasts / unicasts
|
44
|
+
def on_broadcast ws
|
45
|
+
data = ws.data
|
46
|
+
unless (data[:type] || data[:target]) && data[:method] && data[:data]
|
47
|
+
GReactor.warn "Broadcast message unknown... falling back on base broadcasting"
|
48
|
+
return super(data) if defined? super
|
49
|
+
return false
|
50
|
+
end
|
51
|
+
# return false if data[:type] && !self.is_a?(data[:type])
|
52
|
+
return false if data[:target] && data[:target] != ws.uuid
|
53
|
+
return false unless self.class.has_super_method?(data[:method])
|
54
|
+
self.method(data[:method]).call *data[:data]
|
55
|
+
end
|
56
|
+
# Returns the websocket connection's UUID, used for unicasting.
|
57
|
+
def uuid
|
58
|
+
io[:uuid] ||= SecureRandom.uuid
|
59
|
+
end
|
60
|
+
|
61
|
+
# Performs a websocket unicast to the specified target.
|
62
|
+
def unicast target_uuid, method_name, *args
|
63
|
+
GRHttp::Base::WSHandler.unicast target_uuid, data: args, target: target_uuid, method: method_name
|
64
|
+
__send_to_redis data: args, target: target_uuid, method: method_name
|
65
|
+
end
|
66
|
+
# broadcast to a specific controller
|
67
|
+
def broadcast controller_class, method_name, *args
|
68
|
+
GRHttp::Base::WSHandler.broadcast({data: args, type: controller_class, method: method_name}, self)
|
69
|
+
__send_to_redis data: args, type: controller_class, method: method_name
|
70
|
+
end
|
71
|
+
# multicast to all handlers.
|
72
|
+
def multicast method_name, *args
|
73
|
+
GRHttp::Base::WSHandler.broadcast({method: method_name, data: args, type: :all}, self)
|
74
|
+
__send_to_redis method: method_name, data: args, type: :all
|
75
|
+
end
|
76
|
+
protected
|
77
|
+
def __send_to_redis data
|
78
|
+
raise "Wrong method name for websocket broadcasting - expecting type Symbol" unless data[:method].is_a?(Symbol) || data[:method].is_a?(Symbol)
|
79
|
+
conn = Plezi.redis_connection
|
80
|
+
data[:server] = Plezi::Settings.uuid
|
81
|
+
return conn.publish( Plezi::Settings.redis_channel_name, data.to_yaml ) if conn
|
82
|
+
false
|
83
|
+
end
|
84
|
+
end
|
85
|
+
#the methods here will be injected to the Placebo controller as class methods.
|
86
|
+
module ClassMethods
|
87
|
+
public
|
88
|
+
def has_super_method? method_name
|
89
|
+
@super_methods_list ||= self.superclass.instance_methods.to_set
|
90
|
+
@super_methods_list.include? method_name
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
module_function
|
96
|
+
def new placebo_class
|
97
|
+
new_class_name = "PlaceboPlezi__#{placebo_class.name.gsub /[\:\-\#\<\>\{\}\(\)\s]/, '_'}"
|
98
|
+
new_class = nil
|
99
|
+
new_class = Module.const_get new_class_name if Module.const_defined? new_class_name
|
100
|
+
unless new_class
|
101
|
+
new_class = Class.new(placebo_class) do
|
102
|
+
include Placebo::Base::Core
|
103
|
+
end
|
104
|
+
Object.const_set(new_class_name, new_class)
|
105
|
+
end
|
106
|
+
i, o = IO.pipe
|
107
|
+
o.close
|
108
|
+
io = GReactor.add_io i, handler: (Proc.new {|io| io.read })
|
109
|
+
new_class.new(io)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
# class A
|
116
|
+
# def _hi
|
117
|
+
# 'hi'
|
118
|
+
# end
|
119
|
+
# end
|
120
|
+
# Plezi::Placebo.new A
|
121
|
+
# a = nil
|
122
|
+
# GReactor.each {|h| a= h}
|
123
|
+
# a[:websocket_handler].on_broadcast GRHttp::WSEvent.new(nil, type: true, data: [], method: :_hi)
|
data/lib/plezi/handlers/stubs.rb
CHANGED
data/lib/plezi/version.rb
CHANGED
data/plezi.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_dependency "grhttp", "~> 0.0.
|
21
|
+
spec.add_dependency "grhttp", "~> 0.0.11"
|
22
22
|
spec.add_development_dependency "bundler", "~> 1.7"
|
23
23
|
spec.add_development_dependency "rake", "~> 10.0"
|
24
24
|
|
data/test/plezi_tests.rb
CHANGED
@@ -17,6 +17,8 @@ def report_after_filter(result= true)
|
|
17
17
|
true
|
18
18
|
end
|
19
19
|
|
20
|
+
class Nothing
|
21
|
+
end
|
20
22
|
class TestCtrl
|
21
23
|
|
22
24
|
|
@@ -239,6 +241,15 @@ module PleziTestTasks
|
|
239
241
|
puts " **** #url_for test FAILED TO RUN!!!"
|
240
242
|
puts e
|
241
243
|
end
|
244
|
+
def test_placebo
|
245
|
+
puts " * Starting placebo tests..."
|
246
|
+
ws = GRHttp::WSClient.connect_to("ws://localhost:3000/ws/placebo") {|ws| 'ME?'}
|
247
|
+
ws << " * Placebo WS connected."
|
248
|
+
sleep 2
|
249
|
+
rescue => e
|
250
|
+
puts " **** Placebo test FAILED TO RUN!!!"
|
251
|
+
puts e
|
252
|
+
end
|
242
253
|
def test_websocket
|
243
254
|
connection_test = broadcast_test = echo_test = unicast_test = false
|
244
255
|
begin
|
@@ -343,7 +354,38 @@ module PleziTestTasks
|
|
343
354
|
end
|
344
355
|
end
|
345
356
|
|
346
|
-
|
357
|
+
class PlaceboTestCtrl
|
358
|
+
# called when new Websocket data is recieved
|
359
|
+
#
|
360
|
+
# data is a string that contains binary or UTF8 (message dependent) data.
|
361
|
+
def index
|
362
|
+
false
|
363
|
+
end
|
364
|
+
def on_open
|
365
|
+
puts " * Placebo multicasting to placebo test: #{PleziTestTasks::RESULTS[ multicast :send_back, uuid: uuid, test: true, type: 'multicast' ] }"
|
366
|
+
end
|
367
|
+
def on_message data
|
368
|
+
puts data
|
369
|
+
end
|
370
|
+
def _get_uuid data
|
371
|
+
puts " * Placebo send #{data[:type]} test: #{PleziTestTasks::RESULTS[data[:test]]}"
|
372
|
+
unicast( data[:uuid], :send_back, {test: true, type: 'unicast'}) if data[:uuid]
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
class PlaceboCtrl
|
377
|
+
def send_back data
|
378
|
+
puts " * Placebo recieve test for #{data[:type]}: #{PleziTestTasks::RESULTS[data[:test]]}"
|
379
|
+
if data[:uuid]
|
380
|
+
unicast( data[:uuid], :_get_uuid, {test: true, uuid: uuid, type: 'unicast'})
|
381
|
+
else
|
382
|
+
broadcast WSsizeTestCtrl, :_get_uuid, test: true, type: 'broadcast'
|
383
|
+
multicast :_get_uuid, test: true, type: 'multicast'
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
r = Plezi::Placebo.new PlaceboCtrl
|
388
|
+
puts " * Create Placebo test: #{PleziTestTasks::RESULTS[r && true]}"
|
347
389
|
|
348
390
|
PL.create_logger '/dev/null'
|
349
391
|
# PL::Settings.max_threads = 4
|
@@ -354,18 +396,21 @@ route("/ssl") {|req, res| res << "false" }
|
|
354
396
|
listen port: 3030, ssl: true
|
355
397
|
route("/ssl") {|req, res| res << "true" }
|
356
398
|
|
399
|
+
shared_route 'ws/no', Nothing
|
400
|
+
shared_route 'ws/placebo', PlaceboTestCtrl
|
357
401
|
shared_route 'ws/size', WSsizeTestCtrl
|
358
402
|
|
359
403
|
shared_route '/some/:multi{path|another_path}/(:option){route|test}/(:id)/(:optional)', TestCtrl
|
360
404
|
shared_route '/', TestCtrl
|
361
405
|
|
362
406
|
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
407
|
+
Plezi.start_async
|
408
|
+
puts " --- Plezi will ran async, performing some tests that than hang"
|
409
|
+
|
410
|
+
puts " --- Starting tests"
|
411
|
+
puts " --- Failed tests should read: #{PleziTestTasks::RESULTS[false]}"
|
412
|
+
PleziTestTasks.run_tests
|
413
|
+
puts "\n --- Press ^C to complete tests."
|
369
414
|
|
370
415
|
# start_services
|
371
416
|
|
@@ -373,7 +418,6 @@ shoutdown_test = false
|
|
373
418
|
# GReactor::Settings.set_forking 4
|
374
419
|
Plezi.on_shutdown { shoutdown_test = true }
|
375
420
|
|
376
|
-
|
377
421
|
Plezi.start
|
378
422
|
# Plezi::EventMachine.clear_timers
|
379
423
|
|
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.10.
|
4
|
+
version: 0.10.8
|
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-07-
|
11
|
+
date: 2015-07-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: grhttp
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.0.
|
19
|
+
version: 0.0.11
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.0.
|
26
|
+
version: 0.0.11
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -77,6 +77,7 @@ files:
|
|
77
77
|
- lib/plezi/handlers/controller_core.rb
|
78
78
|
- lib/plezi/handlers/controller_magic.rb
|
79
79
|
- lib/plezi/handlers/http_router.rb
|
80
|
+
- lib/plezi/handlers/placebo.rb
|
80
81
|
- lib/plezi/handlers/route.rb
|
81
82
|
- lib/plezi/handlers/stubs.rb
|
82
83
|
- lib/plezi/helpers/http_sender.rb
|