plezi 0.9.2 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -0
  3. data/README.md +44 -31
  4. data/bin/plezi +3 -3
  5. data/lib/plezi.rb +21 -43
  6. data/lib/plezi/common/defer.rb +21 -0
  7. data/lib/plezi/common/dsl.rb +115 -91
  8. data/lib/plezi/common/redis.rb +44 -0
  9. data/lib/plezi/common/settings.rb +58 -0
  10. data/lib/plezi/handlers/controller_core.rb +132 -0
  11. data/lib/plezi/handlers/controller_magic.rb +85 -259
  12. data/lib/plezi/handlers/http_router.rb +139 -60
  13. data/lib/plezi/handlers/route.rb +9 -178
  14. data/lib/plezi/handlers/stubs.rb +2 -2
  15. data/lib/plezi/helpers/http_sender.rb +72 -0
  16. data/lib/plezi/helpers/magic_helpers.rb +12 -0
  17. data/lib/plezi/{server → helpers}/mime_types.rb +0 -0
  18. data/lib/plezi/version.rb +1 -1
  19. data/plezi.gemspec +3 -11
  20. data/resources/Gemfile +20 -21
  21. data/resources/controller.rb +2 -2
  22. data/resources/oauth_config.rb +1 -1
  23. data/resources/redis_config.rb +2 -0
  24. data/test/plezi_tests.rb +39 -46
  25. metadata +24 -33
  26. data/lib/plezi/common/logging.rb +0 -60
  27. data/lib/plezi/eventmachine/connection.rb +0 -190
  28. data/lib/plezi/eventmachine/em.rb +0 -98
  29. data/lib/plezi/eventmachine/io.rb +0 -272
  30. data/lib/plezi/eventmachine/protocol.rb +0 -54
  31. data/lib/plezi/eventmachine/queue.rb +0 -51
  32. data/lib/plezi/eventmachine/ssl_connection.rb +0 -144
  33. data/lib/plezi/eventmachine/timers.rb +0 -117
  34. data/lib/plezi/eventmachine/workers.rb +0 -33
  35. data/lib/plezi/handlers/http_echo.rb +0 -27
  36. data/lib/plezi/handlers/http_host.rb +0 -214
  37. data/lib/plezi/handlers/magic_helpers.rb +0 -32
  38. data/lib/plezi/server/http.rb +0 -129
  39. data/lib/plezi/server/http_protocol.rb +0 -319
  40. data/lib/plezi/server/http_request.rb +0 -146
  41. data/lib/plezi/server/http_response.rb +0 -319
  42. data/lib/plezi/server/websocket.rb +0 -251
  43. data/lib/plezi/server/websocket_client.rb +0 -178
  44. data/lib/plezi/server/ws_response.rb +0 -161
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 42fdb6ed987158652982b125bfe4164b5c2015dc
4
- data.tar.gz: e0ce2fd542ce59a313570197c718fe0f420a94ff
3
+ metadata.gz: 0a5c7776c37ebc623189d2a99863f6571fbef572
4
+ data.tar.gz: 82f7af02e2db159f56d3bd8ee4ce4a2cc4553aca
5
5
  SHA512:
6
- metadata.gz: d04fcf22270263b548c011c0d05be50a5f04962e67e3f81a86ab2ede142312241fc0f7756c3a026d4aa8184a9da5a33c92e0c217187f355978e16ff74b8988a3
7
- data.tar.gz: 6e7cf7932aec26f04663ae84a62eef18f6dcf69a471db99a1befdd67ac30b8453810949e139dfaa3d1d00f5f275f81213bd81aadf70b0426afd694115913b8c3
6
+ metadata.gz: efa0d7353a2e2e4e07a9c52867397731afe670e9fb51a69ba2e7b3b6f07d2ea54985a1037f50b620b06f47768f53285afb7e10023566da60eaa114f8034b64a4
7
+ data.tar.gz: 52fd0e21072250e92837cc85cc236c2054c664c3a86e9517c96b796baca9bb26fb7ff9a243172b28a2f477b1cc6c5b6ae51faa6f3c62311bc2f67c45852febb7
@@ -2,6 +2,36 @@
2
2
 
3
3
  ***
4
4
 
5
+ Change log v.0.10.1
6
+
7
+ **fix**: fixed an issue where the new Controller's inner router might route to RESTful methods that weren't defined (:show, :save, etc').
8
+
9
+ **fix**: fixed an issue with the new Controller's inner router might not reset it's cache when methods are added to the controller after the service has begun.
10
+
11
+ ***
12
+
13
+ Change log v.0.10.0
14
+
15
+ **Major Revesion**:
16
+
17
+ - The Plezi IO Reactor was extracted to an external gem called [GReactor](https://github.com/boazsegev/GReactor) and optimized.
18
+ - The Plezi HTTP and Websocket Server was extracted to an external gem called [GRHttp](https://github.com/boazsegev/GRHttp) and optimized.
19
+ - The Websocket API, implementation and engine were all revised. CAREFUL: **Old Websocket API deprecated**.
20
+
21
+ **WebSocket API revisions**:
22
+
23
+ - The `#on_connect` callback had been renamed to `#on_open`, for clarity and to conform with the Javascript API.
24
+ - The `#on_disconnect` callback had been renamed to `#on_close`, for clarity and to conform with the Javascript API.
25
+ - The `#collect` method had been deprecated due to scaling limitations it had imposed.
26
+ - The `#broadcast` and `Controller.broadcast` methods had been altered and would no longer accept an optional block of code.
27
+ - The Redis support had been altered and the redis connection object (if exists) is now available using `Plezi.redis_connection` instead of the older Controller method.
28
+
29
+ **Settings API revisions**:
30
+
31
+ - The settings API had been moved to the namespace `Plezi::Settings`.
32
+
33
+ ***
34
+
5
35
  Change log v.0.9.2
6
36
 
7
37
  **Some API deprecation notice**
data/README.md CHANGED
@@ -1,20 +1,18 @@
1
1
  # Plezi, The Rack Free Ruby framework for realtime web-apps
2
2
  [![Gem Version](https://badge.fury.io/rb/plezi.svg)](http://badge.fury.io/rb/plezi)
3
- [![Inline docs](http://inch-ci.org/github/boazsegev/plezi.svg?branch=master)](http://inch-ci.org/github/boazsegev/plezi)
3
+ [![Inline docs](http://inch-ci.org/github/boazsegev/plezi.svg?branch=master)](http://www.rubydoc.info/gems/plezi/)
4
4
 
5
5
  > People who are serious about their frameworks, should write their own servers...
6
6
 
7
- Find more info on [Plezi's framework, web server and websocket documentation](http://www.rubydoc.info/gems/plezi/)
7
+ Find more info on [Plezi's framework documentation](http://www.rubydoc.info/gems/plezi/)
8
8
 
9
- ## About the Plezi framework \ server
9
+ ## About the Plezi framework
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
13
  Plezi works as an asynchronous multi-threaded Ruby 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
- Plezi contains an object-oriented server, divided into parts that can be changed/updated and removed easily and dynamically. This allows - much like Node.js - native WebSocket support (and, if you would like to write your own Protocol or Handler, native SMPT or any other custom protocol you might wish to implement).
16
-
17
- You can follow our [tutorial to write your first Plezi Chatroom](http://boazsegev.github.io/plezi/websockets.html) - but it's better to start with this readme and explore the WebSockets example given here.
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).
18
16
 
19
17
  ## Installation
20
18
 
@@ -23,6 +21,7 @@ Add this line to your application's Gemfile:
23
21
  ```ruby
24
22
  gem 'plezi'
25
23
  ```
24
+
26
25
  Or install it yourself as:
27
26
 
28
27
  $ gem install plezi
@@ -33,7 +32,9 @@ to create a new barebones app using the Plezi framework, run from terminal:
33
32
 
34
33
  $ plezi new appname
35
34
 
36
- That's it, now you have a ready to use basic web server (with some demo code). If you're on MacOS or linux you can simply double click the `appname` script file in the `appname` folder. Or, from the terminal, you can type:
35
+ That's it, now you have a ready to use basic web server (with some demo code, such as a websocket chatroom).
36
+
37
+ If you're on MacOS or linux you can simply double click the `appname` script file in the `appname` folder. Or, from the terminal, you can type:
37
38
 
38
39
  $ cd appname
39
40
  $ ./appname # ( or: plezi s )
@@ -44,11 +45,11 @@ the default first port for the app is 3000. you can set the first port to listen
44
45
 
45
46
  $ ./appname -p 80
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
+ 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.
48
49
 
49
50
  ## Barebones Web Service
50
51
 
51
- this example is basic, useless, but required for every doc out there...
52
+ This example is basic, useless, but required for every doc out there...
52
53
 
53
54
  "Hello World!" in 3 lines - try it in irb (exit irb to start server):
54
55
 
@@ -56,11 +57,11 @@ this example is basic, useless, but required for every doc out there...
56
57
  listen
57
58
  route(/.?/) { |req, res| res << "Hello World!" }
58
59
 
59
- After you exited irb, the Plezi server started up. go to http://localhost:3000/ and see it run :)
60
+ After you exit irb, the Plezi server will start up. Go to http://localhost:3000/ and see it run :)
60
61
 
61
62
  ## Plezi Controller classes
62
63
 
63
- 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_connect`, `on_message(data)`, `on_disconnect`, `broadcast`, `collect`):
64
+ 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)`):
64
65
 
65
66
  require 'plezi'
66
67
 
@@ -77,11 +78,11 @@ Except for WebSockets, returning a String will automatically add the string to t
77
78
 
78
79
  Controllers can even be nested (order matters) or have advanced uses that are definitly worth exploring.
79
80
 
80
- **please read the demo code for Plezi::StubRESTCtrl and Plezi::StubWSCtrl to learn more.**
81
+ \* 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.
81
82
 
82
83
  ## Native Websocket and Redis support
83
84
 
84
- Plezi Controllers have access to native websocket support through the `pre_connect`, `on_connect`, `on_message(data)`, `on_disconnect`, `broadcast` and `collect` methods.
85
+ Plezi Controllers have access to native websocket support through the `pre_connect`, `on_open`, `on_message(data)`, `on_close`, `broadcast` and `unicast` methods.
85
86
 
86
87
  Here is some demo code for a simple Websocket broadcasting server, where messages sent to the server will be broadcasted back to all the **other** active connections (the connection sending the message will not recieve the broadcast).
87
88
 
@@ -128,15 +129,18 @@ method names starting with an underscore ('_') will NOT be made public by the ro
128
129
 
129
130
  Plezi comes with native HTTP streaming support, alowing you to use Plezi Events and Timers to send an Asynchronous response.
130
131
 
131
- Let's make the classic 'Hello World' use HTTP Streaming and Asynchronous Plezi Events:
132
+ Let's make the classic 'Hello World' use HTTP Streaming:
132
133
 
133
134
  ```ruby
134
135
  require 'plezi'
135
136
 
136
137
  class Controller
137
138
  def index
138
- response.start_http_streaming
139
- PL.callback(response, :send, "Hello World") { response.finish }
139
+ response.stream_async do
140
+ sleep 0.5
141
+ response << "Hello ";
142
+ response.stream_async{ sleep 0.5; response << "World" }
143
+ end
140
144
  true
141
145
  end
142
146
  end
@@ -145,7 +149,9 @@ Let's make the classic 'Hello World' use HTTP Streaming and Asynchronous Plezi E
145
149
  route '*' , Controller
146
150
  ```
147
151
 
148
- Notice the easy use of Asynchronous Events using the PL#callback method. The optional block passed to this method (`response.finish`) will be executed only after the asynchronous call for the response#send method with the "Hello World" argument has completed.
152
+ Notice you can nest calls to the `response.stream_async` method, allowing you to breakdown big blocking tasks into smaller chunks. `response.stream_async` will return immediately, scheduling the task for background processing.
153
+
154
+ You can also handle other tasks asynchronously using the [GReactor API](http://www.rubydoc.info/gems/greactor)'s.
149
155
 
150
156
  More on asynchronous events and timers later.
151
157
 
@@ -153,7 +159,7 @@ More on asynchronous events and timers later.
153
159
 
154
160
  Plezi supports magic routes, in similar formats found in other systems, such as: `route "/:required/(:optional_with_format){[\\d]*}/(:optional)", Plezi::StubRESTCtrl`.
155
161
 
156
- Plezi assummes all simple string routes to be RESTful routes with the parameter `:id` ( `"/user" == "/user/(:id)"` ).
162
+ Plezi assummes all simple routes to be RESTful routes with the parameter `:id` ( `"/user" == "/user/(:id)"` ).
157
163
 
158
164
  require 'plezi'
159
165
  listen
@@ -172,7 +178,7 @@ now visit:
172
178
 
173
179
  ## Plezi Virtual Hosts
174
180
 
175
- Plezi can be used to create virtual hosts for the same service:
181
+ Plezi can be used to create virtual hosts for the same service, allowing you to handle different domains and subdomains with one app:
176
182
 
177
183
  require 'plezi'
178
184
  listen
@@ -203,7 +209,7 @@ Now visit:
203
209
 
204
210
  ## Plezi Logging
205
211
 
206
- The Plezi module (also `PL`) has methods to help with logging as well as the support you already noticed for dynamic routes, dynamic services and more.
212
+ The Plezi module (also `PL`) delegates to the GReactor methods, helping with logging as well as the support you already noticed for dynamic routes, dynamic services and more.
207
213
 
208
214
  Logging:
209
215
 
@@ -211,6 +217,7 @@ Logging:
211
217
 
212
218
  # simple logging of strings
213
219
  PL.info 'log info'
220
+ GReactor.info 'This is the same, but more direct.'
214
221
  PL.warn 'log warning'
215
222
  PL.error 'log error'
216
223
  PL.fatal "log a fatal error (shuoldn't be needed)."
@@ -223,9 +230,11 @@ Logging:
223
230
  PL.error e
224
231
  end
225
232
 
233
+ Please notice it is faster to use the GReactor API directly when using API that is delegated to GReactor.
234
+
226
235
  ## Plezi Events and Timers
227
236
 
228
- The Plezi module (also `PL`) also has methods to help with asynchronous tasking, callbacks, timers and customized shutdown cleanup.
237
+ The Plezi module (also `PL`) also delegates to the [GReactor's API](http://www.rubydoc.info/gems/greactor/GReactor) to help with asynchronous tasking, callbacks, timers and customized shutdown cleanup.
229
238
 
230
239
  Asynchronous callbacks (works only while services are active and running):
231
240
 
@@ -236,14 +245,17 @@ Asynchronous callbacks (works only while services are active and running):
236
245
  end
237
246
 
238
247
  # shutdown callbacks
239
- PL.on_shutdown(Kernel, :my_shutdown_proc, Time.now) { puts "this will run after shutdown." }
240
- PL.on_shutdown() { puts "this will run too." }
248
+ GReactor.on_shutdown(Kernel, :my_shutdown_proc, Time.now) { puts "this will run after shutdown." }
249
+ GReactor.on_shutdown() { puts "this will run too." }
241
250
 
242
251
  # a timer
243
- PL.run_after 2, -> {puts "this will wait 2 seconds to run... too late. for this example"}
252
+ GReactor.run_after 2, -> {puts "this will wait 2 seconds to run... too late. for this example"}
244
253
 
245
254
  # an asynchronous method call with an optional callback block
246
- PL.callback(Kernel, :puts, "Plezi will start eating our code once we exit terminal.") {puts 'first output finished'}
255
+ GReactor.callback(Kernel, :puts, "Plezi will start eating our code once we exit terminal.") {puts 'first output finished'}
256
+
257
+ GReactor.run_async {puts "notice that the background tasks will only start once the Plezi's engine is running."}
258
+ GReactor.run_async {puts "exit Plezi to observe the shutdown callbacks."}
247
259
 
248
260
  ## Re-write Routes
249
261
 
@@ -313,12 +325,6 @@ As you can see in the example above, Plezi supports Proc routes as well as Class
313
325
 
314
326
  Please notice that there are some differences between the two. Proc routes less friedly, but plenty powerful and are great for custom 404 error handling.
315
327
 
316
- ## Plezi Settings
317
-
318
- Plezi is ment to be very flexible. please take a look at the Plezi Module for settings you might want to play with (max_threads, idle_sleep, create_logger) or any monkey patching you might enjoy.
319
-
320
- Feel free to fork or contribute. right now I am one person, but together we can make something exciting that will help us enjoy Ruby in this brave new world and (hopefully) set an example that will induce progress in the popular mainstream frameworks such as Rails and Sinatra.
321
-
322
328
  ## OAuth2 and other Helpers
323
329
 
324
330
  Plezi has a few helpers that help with common tasks.
@@ -366,6 +372,13 @@ Plezi has a some more goodies under the hood.
366
372
 
367
373
  Whether such goodies are part of the Plezi-App Template (such as rake tasks for ActiveRecord without Rails) or part of the Plezi Framework core (such as descried in the Plezi::ControllerMagic documentation: #flash, #url_for, #render, #send_data, etc'), these goodies are fun to work with and make completion of common tasks a breeze.
368
374
 
375
+
376
+ ## Plezi Settings
377
+
378
+ Plezi is meant to be very flexible. please take a look at the Plezi Module for settings you might want to play with (max_threads, idle_sleep, create_logger) or any monkey patching you might enjoy.
379
+
380
+ Feel free to fork or contribute. right now I am one person, but together we can make something exciting that will help us enjoy Ruby in this brave new world and (hopefully) set an example that will induce progress in the popular mainstream frameworks such as Rails and Sinatra.
381
+
369
382
  ## Contributing
370
383
 
371
384
  1. Fork it ( https://github.com/boazsegev/plezi/fork )
data/bin/plezi CHANGED
@@ -76,9 +76,9 @@ class AppTemplate
76
76
  # set up config files
77
77
  app_tree["config"] ||= {}
78
78
  app_tree["config"]["oauth.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"oauth_config.rb"), __FILE__))
79
- app_tree["config"]["db_active_record.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"db_ac_config.rb"), __FILE__))
80
- app_tree["config"]["db_sequel.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"db_sequel_config.rb"), __FILE__))
81
- app_tree["config"]["db_datamapper.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"db_dm_config.rb"), __FILE__))
79
+ app_tree["config"]["active_record.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"db_ac_config.rb"), __FILE__))
80
+ app_tree["config"]["sequel.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"db_sequel_config.rb"), __FILE__))
81
+ app_tree["config"]["datamapper.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"db_dm_config.rb"), __FILE__))
82
82
  app_tree["config"]["haml.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"haml_config.rb"), __FILE__))
83
83
  app_tree["config"]["i18n.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"i18n_config.rb"), __FILE__))
84
84
  app_tree["config"]["redis.rb"] ||= (IO.read(::File.expand_path(File.join("..", "..", "resources" ,"redis_config.rb"), __FILE__))).gsub('appsecret', "#{ARGV[1]}_#{SecureRandom.hex}")
@@ -2,73 +2,51 @@
2
2
 
3
3
  require 'singleton'
4
4
  require 'pathname'
5
- require 'logger'
6
- require 'socket'
7
- require 'openssl'
8
- require 'strscan'
9
5
  require 'base64'
10
6
  require 'digest/sha1'
11
7
  require 'securerandom'
12
8
  require 'time'
13
9
  require 'json'
10
+ require 'yaml'
14
11
  require 'uri'
15
12
  require 'set'
16
13
 
14
+ # GRHttp servet
15
+ require 'grhttp'
16
+
17
+
17
18
  ## erb templating
18
19
  begin
19
20
  require 'erb'
20
- rescue Exception => e
21
-
21
+ rescue => e
22
+
22
23
  end
23
24
 
24
25
  ### version
25
26
 
26
27
  require "plezi/version"
27
28
 
28
- ### common helpers
29
+ ### common
29
30
 
30
- require 'plezi/common/logging.rb'
31
+ require 'plezi/common/defer.rb'
31
32
  require 'plezi/common/cache.rb'
32
33
  require 'plezi/common/dsl.rb'
34
+ require 'plezi/common/redis.rb'
35
+ require 'plezi/common/settings.rb'
33
36
 
34
- ### event machine
35
-
36
- require "plezi/eventmachine/em.rb"
37
-
38
- require 'plezi/eventmachine/queue.rb'
39
- require 'plezi/eventmachine/workers.rb'
40
- require 'plezi/eventmachine/timers.rb'
41
- require 'plezi/eventmachine/io.rb'
42
- require 'plezi/eventmachine/protocol.rb'
43
- require 'plezi/eventmachine/connection.rb'
44
- require 'plezi/eventmachine/ssl_connection.rb'
45
-
46
- ### http and websocket server
47
-
48
-
49
- require 'plezi/server/mime_types.rb'
50
-
51
- require 'plezi/server/http.rb'
52
- require 'plezi/server/http_protocol.rb'
53
- require 'plezi/server/http_request.rb'
54
- require 'plezi/server/http_response.rb'
55
-
56
- require 'plezi/server/websocket.rb'
57
- require 'plezi/server/ws_response.rb'
58
-
59
- ### websocket client
60
- require 'plezi/server/websocket_client.rb'
61
- ### Handlers / Framework
37
+ ### helpers
62
38
 
63
- require "plezi/handlers/http_echo"
64
- require "plezi/handlers/http_host"
65
- require "plezi/handlers/http_router"
39
+ require 'plezi/helpers/http_sender.rb'
40
+ require 'plezi/helpers/magic_helpers.rb'
41
+ require 'plezi/helpers/mime_types.rb'
66
42
 
67
- require "plezi/handlers/controller_magic"
68
- require "plezi/handlers/magic_helpers"
69
- require "plezi/handlers/route"
70
43
 
71
- require "plezi/handlers/stubs"
44
+ ### HTTP and WebSocket Handlers
45
+ require 'plezi/handlers/http_router.rb'
46
+ require 'plezi/handlers/route.rb'
47
+ require 'plezi/handlers/controller_magic.rb'
48
+ require 'plezi/handlers/controller_core.rb'
49
+ require 'plezi/handlers/stubs.rb'
72
50
 
73
51
 
74
52
  ##############################################################################
@@ -0,0 +1,21 @@
1
+
2
+ module Plezi
3
+
4
+ module_function
5
+
6
+ # Defers any missing methods to the GReactor Library.
7
+ def method_missing name, *args, &block
8
+ return super unless REACTOR_METHODS.include? name
9
+ ::GReactor.send name, *args, &block
10
+ end
11
+ # Defers any missing methods to the GReactor Library.
12
+ def respond_to_missing?(name, include_private = false)
13
+ REACTOR_METHODS.include?(name) or super
14
+ end
15
+
16
+ protected
17
+
18
+ REACTOR_METHODS = ::GReactor.public_methods(false)
19
+
20
+ end
21
+
@@ -1,109 +1,129 @@
1
1
 
2
2
  module Plezi
3
3
 
4
- # holds methods that are called by the DSL.
5
- #
6
4
  # this isn't part of the public API.
7
- module DSL
8
- module_function
5
+ module Base
9
6
 
10
- # this module contains the methods that are used as a DSL and sets up easy access to the Plezi framework.
7
+ # holds methods that are called by the DSL.
11
8
  #
12
- # use the`listen`, `host` and `route` functions rather then accessing this object.
13
- #
14
- @servers = {}
15
- @active_router = nil
16
-
17
- # adds a route to the last server created
18
- def route(path, controller = nil, &block)
19
- @active_router.add_route path, controller, &block
20
- end
21
-
22
-
23
- # adds a shared route to all existing services and hosts.
24
- def shared_route(path, controller = nil, &block)
25
- @listeners.values.each {|p| (defined?(PLEZI_ON_RACK) ? p[:handler] : p.params[:handler]).add_shared_route path, controller, &block }
26
- end
27
-
28
- # adds a host to the last server created
29
- #
30
- # accepts the same parameter(s) as the `listen` command (see Plezi.add_service), except :protocol and :handler are ignored:
31
- # alias:: a String or an Array of Strings which represent alternative host names (i.e. `alias: ["admin.google.com", "admin.gmail.com"]`).
32
- def host(host_name, params)
33
- @active_router.add_host host_name, params
34
- end
35
-
36
- # Rack application model support
37
-
38
- # Plezi dresses up for Rack - this is a watered down version missing some features (such as flash and WebSockets).
39
- # a full featured Plezi app, with WebSockets, requires the use of the Plezi server
40
- # (the built-in server)
41
- def call env
42
- raise "No Plezi Services" unless @listeners && @listeners.any?
43
-
44
- # re-encode to utf-8, as it's all BINARY encoding at first
45
- env['rack.input'].rewind
46
- env['rack.input'] = StringIO.new env['rack.input'].read.encode('utf-8', 'binary', invalid: :replace, undef: :replace, replace: '')
47
- env.each do |k, v|
48
- if k.to_s.match /^[A-Z]/
49
- if v.is_a?(String) && !v.frozen?
50
- v.force_encoding('binary').encode!('utf-8', 'binary', invalid: :replace, undef: :replace, replace: '') unless v.force_encoding('utf-8').valid_encoding?
9
+ # this isn't part of the public API.
10
+ module DSL
11
+ module_function
12
+
13
+ # this module contains the methods that are used as a DSL and sets up easy access to the Plezi framework.
14
+ #
15
+ # use the`listen`, `host` and `route` functions rather then accessing this object.
16
+ #
17
+ @servers = {}
18
+ @active_router = nil
19
+
20
+
21
+ # public API to add a service to the framework.
22
+ # accepts a Hash object with any of the following options (Hash keys):
23
+ # port:: port number. defaults to 3000 or the port specified when the script was called.
24
+ # host:: the host name. defaults to any host not explicitly defined (a catch-all).
25
+ # alias:: a String or an Array of Strings which represent alternative host names (i.e. `alias: ["admin.google.com", "admin.gmail.com"]`).
26
+ # root:: the public root folder. if this is defined, static files will be served from the location.
27
+ # 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).
28
+ # 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.
29
+ # 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.
30
+ # save_assets:: saves the rendered assets to the filesystem, under the public folder. defaults to false.
31
+ # templates:: the templates root folder. defaults to nil (no template support). templates can be rendered by a Controller class, using the `render` method.
32
+ # ssl:: if true, an SSL service will be attempted. if no certificate is defined, an attempt will be made to create a self signed certificate.
33
+ # ssl_key:: the public key for the SSL service.
34
+ # ssl_cert:: the certificate for the SSL service.
35
+ #
36
+ # some further options, which are unstable and might be removed in future versions, are:
37
+ # protocol:: the protocol objects (usually a class, but any object answering `#call` will do).
38
+ # handler:: an optional handling object, to be called upon by the protocol (i.e. #on_message, #on_connect, etc'). this option is used to allow easy protocol switching, such as from HTTP to Websockets.
39
+ #
40
+ # Duringn normal Plezi behavior, the optional `handler` object will be returned if `listen` is called more than once for the same port.
41
+ #
42
+ # assets:
43
+ #
44
+ # assets support will render `.sass`, `.scss` and `.coffee` and save them as local files (`.css`, `.css`, and `.js` respectively)
45
+ # before sending them as static files.
46
+ #
47
+ # templates:
48
+ #
49
+ # ERB, Slim and Haml are natively supported.
50
+ #
51
+ # @returns [Plezi::Router]
52
+ #
53
+ def listen parameters = {}
54
+ # update default values
55
+ parameters[:index_file] ||= 'index.html'
56
+ parameters[:assets_public] ||= '/assets'
57
+ parameters[:assets_public].chomp! '/'
58
+
59
+ #keeps information of past ports.
60
+ @listeners ||= {}
61
+ @listeners_locker = Mutex.new
62
+
63
+ # check if the port is used twice.
64
+ @listeners_locker.synchronize do
65
+ if @listeners[parameters[:port]]
66
+ puts "WARNING: port aleady in use! returning existing service and attemptin to add host (maybe multiple hosts? use `host` instead)."
67
+ @active_router = @listeners[parameters[:port]].params[:http_handler]
68
+ @active_router.add_host parameters[:host], parameters if @active_router.is_a?(HTTPRouter)
69
+ return @active_router
51
70
  end
52
71
  end
53
- end
54
- # re-key params
55
- # new_params = {}
56
- # env[:params].each {|k,v| HTTP.add_param_to_hash k, v, new_params}
57
- # env[:params] = new_params
72
+ @listeners[parameters[:port]] = parameters
58
73
 
59
- # make hashes magical
60
- make_hash_accept_symbols(env)
74
+ # make sure the protocol exists.
75
+ parameters[:upgrade_handler] = parameters[:http_handler] = HTTPRouter.new
61
76
 
62
- # use Plezi Cookies
63
- env['rack.request.cookie_string'] = env['HTTP_COOKIE']
64
- env['rack.request.cookie_hash'] = Plezi::Cookies.new.update(env['rack.request.cookie_hash'] || {})
77
+ GRHttp.listen parameters
78
+ # set the active router to the handler or the protocol.
79
+ @active_router = parameters[:http_handler]
80
+ @active_router.add_host(parameters[:host], parameters)
65
81
 
66
- # chomp path
67
- env['PATH_INFO'].chomp! '/'
68
-
69
- # get response
70
- response = @listeners.first[1][:handler].call env
82
+ # return the current handler or the protocol..
83
+ @active_router
84
+ end
85
+ # adds a route to the last server created
86
+ def route(path, controller = nil, &block)
87
+ raise "Must define a listener before adding a route - use `Plezi.listen`." unless @active_router
88
+ @active_router.add_route path, controller, &block
89
+ end
71
90
 
72
- return response if response.is_a?(Array)
73
91
 
74
- return [404, {}, ['not found']] if response === true
92
+ # adds a shared route to all existing services and hosts.
93
+ def shared_route(path, controller = nil, &block)
94
+ raise "Must have created at least one Pleze service before calling `shared_route` - use `Plezi.listen`." unless @listeners
95
+ @listeners.values.each {|p| p[:http_handler].add_shared_route path, controller, &block }
96
+ end
75
97
 
76
- response.prep_rack
77
- headers = response.headers
78
- # headers.delete 'transfer-encoding'
79
- # headers.delete 'connection'
80
- # set cookie headers
81
- unless response.cookies.empty?
82
- headers['Set-Cookie'] = []
83
- response.cookies.each {|k,v| headers['Set-Cookie'] << ("#{k.to_s}=#{v.to_s}")}
98
+ # adds a host to the last server created
99
+ #
100
+ # accepts the same parameter(s) as the `listen` command (see Plezi.add_service), except :protocol and :handler are ignored:
101
+ # alias:: a String or an Array of Strings which represent alternative host names (i.e. `alias: ["admin.google.com", "admin.gmail.com"]`).
102
+ def host(host_name, params)
103
+ raise "Must define a listener before adding a route - use `Plezi.listen`." unless @active_router
104
+ @active_router.add_host host_name, params
84
105
  end
85
- [response.status, headers, response.body]
86
- end
87
106
 
88
- # tweeks a hash object to read both :symbols and strings (similar to Rails but without).
89
- def make_hash_accept_symbols hash
90
- @magic_hash_proc ||= Proc.new do |hs,k|
91
- if k.is_a?(Symbol) && hs.has_key?( k.to_s)
92
- hs[k.to_s]
93
- elsif k.is_a?(String) && hs.has_key?( k.to_sym)
94
- hs[k.to_sym]
95
- elsif k.is_a?(Numeric) && hs.has_key?(k.to_s.to_sym)
96
- hs[k.to_s.to_sym]
107
+
108
+ # tweeks a hash object to read both :symbols and strings (similar to Rails but without).
109
+ def make_hash_accept_symbols hash
110
+ @magic_hash_proc ||= Proc.new do |hs,k|
111
+ if k.is_a?(Symbol) && hs.has_key?( k.to_s)
112
+ hs[k.to_s]
113
+ elsif k.is_a?(String) && hs.has_key?( k.to_sym)
114
+ hs[k.to_sym]
115
+ elsif k.is_a?(Numeric) && hs.has_key?(k.to_s.to_sym)
116
+ hs[k.to_s.to_sym]
117
+ end
97
118
  end
98
- end
99
- hash.default_proc = @magic_hash_proc
100
- hash.values.each do |v|
101
- if v.is_a?(Hash)
102
- make_hash_accept_symbols v
119
+ hash.default_proc = @magic_hash_proc
120
+ hash.values.each do |v|
121
+ if v.is_a?(Hash)
122
+ make_hash_accept_symbols v
123
+ end
103
124
  end
104
125
  end
105
126
  end
106
-
107
127
  end
108
128
  end
109
129
 
@@ -116,7 +136,7 @@ PL = Plezi
116
136
  # shortcut for Plezi::DSL.listen.
117
137
  #
118
138
  def listen(params = {})
119
- Plezi::DSL.listen params
139
+ Plezi::Base::DSL.listen params
120
140
  end
121
141
 
122
142
  # adds a virtul host to the current service (the last `listen` call) or switches to an existing host within the active service.
@@ -125,7 +145,7 @@ end
125
145
  # host_name: a String with the full host name (i.e. "www.google.com" / "mail.google.com")
126
146
  # params:: any of the parameters accepted by the `listen` command, except `protocol`, `handler`, and `ssl` parameters.
127
147
  def host(host_name = false, params = {})
128
- Plezi::DSL.host host_name, params
148
+ Plezi::Base::DSL.host host_name, params
129
149
  end
130
150
 
131
151
  # adds a route to the last server object
@@ -162,14 +182,14 @@ end
162
182
  # json serving apps are advised to use required parameters, empty sections indicating missing required parameters (i.e. /path///foo/bar///).
163
183
  #
164
184
  def route(path, controller = nil, &block)
165
- Plezi::DSL.route(path, controller, &block)
185
+ Plezi::Base::DSL.route(path, controller, &block)
166
186
  end
167
187
 
168
188
  # adds a route to the all the existing servers and hosts.
169
189
  #
170
190
  # accepts same options as route.
171
191
  def shared_route(path, controller = nil, &block)
172
- Plezi::DSL.shared_route(path, controller, &block)
192
+ Plezi::Base::DSL.shared_route(path, controller, &block)
173
193
  end
174
194
 
175
195
  # defines a method with a special name, such as "humens.txt".
@@ -199,7 +219,11 @@ def start_services
199
219
  undef route
200
220
  undef shared_route
201
221
  undef start_services
202
- Plezi::DSL.start_services
222
+ puts "Starting Plezi #{Plezi::VERSION} Services using the GRHttp #{GRHttp::VERSION} server."
223
+ puts "Press ^C to exit."
224
+ GReactor.on_shutdown { puts "Plezi shutdown. It was fun to serve you." }
225
+ GReactor.start unless GReactor.running?
226
+ GReactor.join { puts "\r\nStarting shutdown sequesnce. Press ^C to force quit."}
203
227
  end
204
228
 
205
229
  # restarts the Plezi app with the same arguments as when it was started.