plezi 0.11.2 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/README.md +32 -33
- data/docs/async_helpers.md +90 -40
- data/docs/logging.md +38 -3
- data/docs/routes.md +177 -5
- data/docs/websockets.md +5 -0
- data/lib/plezi.rb +2 -10
- data/lib/plezi/common/api.rb +48 -101
- data/lib/plezi/common/defer.rb +4 -4
- data/lib/plezi/common/dsl.rb +13 -24
- data/lib/plezi/common/redis.rb +9 -5
- data/lib/plezi/common/settings.rb +4 -24
- data/lib/plezi/handlers/controller_core.rb +7 -14
- data/lib/plezi/handlers/controller_magic.rb +12 -10
- data/lib/plezi/handlers/http_router.rb +27 -22
- data/lib/plezi/handlers/placebo.rb +40 -33
- data/lib/plezi/handlers/route.rb +233 -234
- data/lib/plezi/handlers/session.rb +2 -2
- data/lib/plezi/handlers/stubs.rb +7 -0
- data/lib/plezi/handlers/ws_object.rb +25 -15
- data/lib/plezi/helpers/http_sender.rb +3 -3
- data/lib/plezi/helpers/magic_helpers.rb +3 -3
- data/lib/plezi/version.rb +1 -1
- data/plezi.gemspec +1 -1
- data/resources/config.ru +12 -18
- data/resources/environment.rb +6 -7
- data/resources/mini_app.rb +22 -22
- data/resources/redis_config.rb +4 -2
- data/test/plezi_tests.rb +76 -87
- data/websocket chatroom.md +3 -5
- metadata +6 -6
- data/docs/http_helpers.md +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c3641815744ffb10926c4e95a6fd47a7fcb4ba1f
|
4
|
+
data.tar.gz: a44e5be3f88040d8d46ff5adf6469fd3efad66c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 88072db2e3f71acb5a5b5e96764f285f20e7b8a0194e07122b24fd06e46fec9b38aa98c0a27a9c6551c3e30625b7568f8bb2765db752e20dd836da371c4bfe18
|
7
|
+
data.tar.gz: 77c52cfba2bfb6e2ad0c7c7a80dd0d2d84a8d0893327ccb983406b0b4e049fcbbf4134dbbdb22517ed46f19ac4d1b6093b9701c0ab6ad223cb537a31303a2da5
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,26 @@
|
|
2
2
|
|
3
3
|
***
|
4
4
|
|
5
|
+
Change log v.0.12.0 - API changes (throwing out dead code)
|
6
|
+
|
7
|
+
**Feature** The `Controller.failed_unicast(target, method, arguments_array)` callback is here, allowing you to write a class level callback that will be called if `unicast` fails to find it's target (i.e. if the Websocket connection was already closed or the hosting server shutdown).
|
8
|
+
|
9
|
+
\* the lack of this callback being called does NOT imply that the unicast was processed without errors, it's only called if the target itself wasn't found (the connection already recognized as closed). Errors can occure within the method originally called by `unicast` when the target was found but the connection was dropped while processing was underway. The `failed_unicast` callback, together with error handling in the original method (i.e. `response << "message"` returning `nil`) should cover any reasonable scenario.
|
10
|
+
|
11
|
+
**Minor**: updated asset pipeline performance; API for the `Plezi.route` methods now auto-creates an empty listening service (no assets, no templates, no public folder...) if one is missing.
|
12
|
+
|
13
|
+
**Fix**: The '/*' is automatically appended to the Re-Write routes, so now writing re-write routes is easier and more intuitive.
|
14
|
+
|
15
|
+
**Fix**: fixed issue with the Placebo API that could cause CPU cycles (IO.select would return immediately) and an issue where the on_close callback wouldn't be called.
|
16
|
+
|
17
|
+
**Big Change**: Discarded GReactor and GRHttp in favor of [Iodine](https://github.com/boazsegev/iodine) - an Object Oriented IO Reactor for writing network services, which includes an optional Http, Websocket and even an experimental Http/2 server (all to show off it's the ability to change protocols mid-stream).
|
18
|
+
|
19
|
+
**API changes**: Along with moving to a single server Iodine module, `listen` had been deprecated in favor of a simpler API, as well as many other helpers that were acting as dead-code.
|
20
|
+
|
21
|
+
**Fix**: Along with switching to Iodine, certain server related issues were fixed (such as String and Symbol cookies with unexpected behavior).
|
22
|
+
|
23
|
+
***
|
24
|
+
|
5
25
|
Change log v.0.11.2
|
6
26
|
|
7
27
|
**Fix**: Fixed an issue where the Session object wouldn't be available for websocket connections after the handshake was complete.
|
data/README.md
CHANGED
@@ -10,7 +10,9 @@ With Plezi, you can easily:
|
|
10
10
|
|
11
11
|
3. Create a full fledged Ruby web application, taking full advantage of RESTful routing, HTTP streaming and scalable Websocket features.
|
12
12
|
|
13
|
-
Plezi leverages [
|
13
|
+
Plezi leverages [Iodine's server](https://github.com/boazsegev/iodine) new architecture. Iodine is a pure Ruby HTTP and Websocket Server built using [Iodine's](https://github.com/boazsegev/iodine) core library - a multi-threaded pure ruby alternative to EventMachine with process forking support (enjoy forking, if your code is scaling ready).
|
14
|
+
|
15
|
+
Plezi and Iodine are written for Ruby versions 2.1.0 or greater (or API compatible variants). Version 2.2.3 is the currently recommended version.
|
14
16
|
|
15
17
|
### Plezi version data
|
16
18
|
[![Gem Version](https://badge.fury.io/rb/plezi.svg)](http://badge.fury.io/rb/plezi)
|
@@ -45,7 +47,7 @@ If you're on MacOS or linux you can simply double click the `appname` script fil
|
|
45
47
|
|
46
48
|
now go, in your browser, to: [http://localhost:3000/](http://localhost:3000/)
|
47
49
|
|
48
|
-
the default
|
50
|
+
the default port for the app is 3000. you can set the port to listen to by using the `-p ` option (make sure you have permissions for the requested port):
|
49
51
|
|
50
52
|
$ ./appname -p 80
|
51
53
|
|
@@ -65,7 +67,12 @@ Here is a Hello World using a Controller class (run in `irb`):
|
|
65
67
|
end
|
66
68
|
end
|
67
69
|
|
68
|
-
|
70
|
+
# use the host method to set up any specific host options:
|
71
|
+
host :default,
|
72
|
+
public: File.join('my', 'public', 'folder'),
|
73
|
+
templates: File.join('my', 'template', 'folder')
|
74
|
+
|
75
|
+
|
69
76
|
route '*' , Controller
|
70
77
|
|
71
78
|
exit # Plezi will autostart once you exit irb.
|
@@ -74,7 +81,7 @@ Except while using WebSockets, returning a String will automatically add the str
|
|
74
81
|
|
75
82
|
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
83
|
|
77
|
-
\* please read the demo code for Plezi::StubRESTCtrl and Plezi::StubWSCtrl to learn more. Also, read more about the [
|
84
|
+
\* please read the demo code for Plezi::StubRESTCtrl and Plezi::StubWSCtrl to learn more. Also, read more about the [Iodine's Websocket and HTTP server](https://github.com/boazsegev/iodine) at the core of Plezi to get more information about the amazing [Request](http://www.rubydoc.info/github/boazsegev/iodine/master/Iodine/Http/Request) and [Response](http://www.rubydoc.info/github/boazsegev/iodine/master/Iodine/Http/Response) objects.
|
78
85
|
|
79
86
|
## Native Websocket and Redis support
|
80
87
|
|
@@ -114,8 +121,6 @@ Remember to connect to the service from at least two browser windows - to truly
|
|
114
121
|
end
|
115
122
|
end
|
116
123
|
|
117
|
-
listen
|
118
|
-
|
119
124
|
route '/', BroadcastCtrl
|
120
125
|
```
|
121
126
|
|
@@ -143,7 +148,7 @@ There are two easy ways to add Plezi websockets to your existing WebApp, dependi
|
|
143
148
|
|
144
149
|
### The super easy way - a Hybrid app
|
145
150
|
|
146
|
-
The easiest way to add Plezi websockets to your existing application is to use [
|
151
|
+
The easiest way to add Plezi websockets to your existing application is to use [Iodine's](https://github.com/boazsegev/iodine) Rack adapter to run your Rack app, while Plezi will use Iodine's native features (such as Websockets and HTTP streaming).
|
147
152
|
|
148
153
|
You can eaither use your existing Plezi application or create a new mini plezi application inside your existing app folder using:
|
149
154
|
|
@@ -153,13 +158,14 @@ Next, add the `plezi` gem to your `Gemfile` and add the following line somewhere
|
|
153
158
|
|
154
159
|
```ruby
|
155
160
|
require './appname/appname.rb'
|
156
|
-
Plezi.start_rack
|
157
161
|
```
|
158
162
|
|
159
163
|
That's it! Now you can use the Plezi API and your existing application's API at the same time and they are both running on the same server.
|
160
164
|
|
161
165
|
Plezi's routes will be attempted first, so that your app can keep handling the 404 (not found) error page.
|
162
166
|
|
167
|
+
\* just remember to remove any existing servers, such as `thin` of `puma` from your gemfile, otherwise they might take precedence over Plezi's choice of server (Iodine).
|
168
|
+
|
163
169
|
### The Plezi Placebo API - talking from afar
|
164
170
|
|
165
171
|
To use Plezi and your App on different processes, without mixing them together, 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, but Plezi will not interfere with your WebApp in any way.
|
@@ -173,6 +179,7 @@ require './my_plezi_app/routes.rb'
|
|
173
179
|
|
174
180
|
# # Make sure the following is already in your 'my_plezi_app/environment.rb' file:
|
175
181
|
# ENV['PL_REDIS_URL'] = "redis://username:password@my.host:6379"
|
182
|
+
# Plezi::Settings.redis_channel_name = 'unique_channel_name_for_app_b24270e2'
|
176
183
|
|
177
184
|
Plezi.start_placebo
|
178
185
|
```
|
@@ -219,10 +226,10 @@ class MyReciever
|
|
219
226
|
# your app's logic
|
220
227
|
end
|
221
228
|
end
|
222
|
-
Plezi
|
229
|
+
Plezi.start_placebo MyReciever
|
223
230
|
```
|
224
231
|
|
225
|
-
Plezi will now take your class and add mimick an IO connection (the Placebo connection) on it's
|
232
|
+
Plezi will now take your class and add mimick an IO connection (the Placebo connection) on it's Iodine serever. This Placebo connection will answer the Redis broadcasts just as if your class was a websocket controller...
|
226
233
|
|
227
234
|
On the Plezi side, use multicasting or unicasting (but not broadcasting), from ANY controller:
|
228
235
|
|
@@ -250,7 +257,7 @@ class MyReciever
|
|
250
257
|
end
|
251
258
|
end
|
252
259
|
|
253
|
-
pl = Plezi
|
260
|
+
pl = Plezi.start_placebo MyReciever
|
254
261
|
|
255
262
|
Plezi.redis_connection.set 'MainUUIDs', pl.uuid
|
256
263
|
|
@@ -270,7 +277,7 @@ end
|
|
270
277
|
|
271
278
|
## Native HTTP streaming with Asynchronous events
|
272
279
|
|
273
|
-
Plezi comes with native HTTP streaming support, alowing you to use Plezi Events and Timers to send an Asynchronous response.
|
280
|
+
Plezi comes with native HTTP streaming support (Http will use chuncked encoding unless experimental Http/2 is in use), alowing you to use Plezi Events and Timers to send an Asynchronous response.
|
274
281
|
|
275
282
|
Let's make the classic 'Hello World' use HTTP Streaming:
|
276
283
|
|
@@ -288,13 +295,12 @@ Let's make the classic 'Hello World' use HTTP Streaming:
|
|
288
295
|
end
|
289
296
|
end
|
290
297
|
|
291
|
-
listen
|
292
298
|
route '*' , Controller
|
293
299
|
```
|
294
300
|
|
295
301
|
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.
|
296
302
|
|
297
|
-
You can also handle other tasks asynchronously using the [
|
303
|
+
You can also handle other tasks asynchronously using the [Iodine's API](http://www.rubydoc.info/gems/iodine).
|
298
304
|
|
299
305
|
More on asynchronous events and timers later.
|
300
306
|
|
@@ -305,7 +311,6 @@ Plezi supports magic routes, in similar formats found in other systems, such as:
|
|
305
311
|
Plezi assummes all simple routes to be RESTful routes with the parameter `:id` ( `"/user" == "/user/(:id)"` ).
|
306
312
|
|
307
313
|
require 'plezi'
|
308
|
-
listen
|
309
314
|
|
310
315
|
# this route demos a route for listing/showing posts,
|
311
316
|
# with or without revision numbers or page-control....
|
@@ -324,8 +329,8 @@ now visit:
|
|
324
329
|
Plezi can be used to create virtual hosts for the same service, allowing you to handle different domains and subdomains with one app:
|
325
330
|
|
326
331
|
require 'plezi'
|
327
|
-
|
328
|
-
host 'localhost', alias: 'localhost2'
|
332
|
+
|
333
|
+
host 'localhost', alias: 'localhost2', public: File.join('my', 'public', 'folder')
|
329
334
|
|
330
335
|
shared_route '/humans.txt' do |req, res|
|
331
336
|
res << "we are people - shared by all routes."
|
@@ -349,10 +354,11 @@ Now visit:
|
|
349
354
|
* [http://localhost:3000/]( http://localhost:3000/ )
|
350
355
|
* [http://127.0.0.1:3000/humans.txt]( http://127.0.0.1:3000/humans.txt )
|
351
356
|
* [http://localhost:3000/humans.txt]( http://localhost:3000/humans.txt )
|
357
|
+
* notice: `localhost2` will only work if it was defined in your OS's `hosts` file.
|
352
358
|
|
353
359
|
## Plezi Logging
|
354
360
|
|
355
|
-
The Plezi module (also `PL`) delegates to the
|
361
|
+
The Plezi module (also `PL`) delegates to the Iodine methods, helping with logging as well as the support you already noticed for dynamic routes, dynamic services and more.
|
356
362
|
|
357
363
|
Logging:
|
358
364
|
|
@@ -360,7 +366,7 @@ Logging:
|
|
360
366
|
|
361
367
|
# simple logging of strings
|
362
368
|
PL.info 'log info'
|
363
|
-
|
369
|
+
Iodine.info 'This is the same, but more direct.'
|
364
370
|
PL.warn 'log warning'
|
365
371
|
PL.error 'log error'
|
366
372
|
PL.fatal "log a fatal error (shuoldn't be needed)."
|
@@ -373,11 +379,11 @@ Logging:
|
|
373
379
|
PL.error e
|
374
380
|
end
|
375
381
|
|
376
|
-
Please notice it is faster to use the
|
382
|
+
Please notice it is faster to use the Iodine's API directly when using API that is delegated to Iodine.
|
377
383
|
|
378
384
|
## Plezi Events and Timers
|
379
385
|
|
380
|
-
The Plezi module (also `PL`) also delegates to the [
|
386
|
+
The Plezi module (also `PL`) also delegates to the [Iodine's API](http://www.rubydoc.info/gems/greactor/iodine) to help with asynchronous tasking, callbacks, timers and customized shutdown cleanup.
|
381
387
|
|
382
388
|
Asynchronous callbacks (works only while services are active and running):
|
383
389
|
|
@@ -388,17 +394,14 @@ Asynchronous callbacks (works only while services are active and running):
|
|
388
394
|
end
|
389
395
|
|
390
396
|
# shutdown callbacks
|
391
|
-
|
392
|
-
|
397
|
+
Iodine.on_shutdown(Kernel, :my_shutdown_proc, Time.now) { puts "this will run after shutdown." }
|
398
|
+
Iodine.on_shutdown() { puts "this will run too." }
|
393
399
|
|
394
400
|
# a timer
|
395
|
-
|
396
|
-
|
397
|
-
# an asynchronous method call with an optional callback block
|
398
|
-
GReactor.callback(Kernel, :puts, "Plezi will start eating our code once we exit terminal.") {puts 'first output finished'}
|
401
|
+
Iodine.run_after(2) {puts "this will wait 2 seconds to run... too late. for this example"}
|
399
402
|
|
400
|
-
|
401
|
-
|
403
|
+
Iodine.run {puts "notice that the background tasks will only start once the Plezi's engine is running."}
|
404
|
+
Iodine.run {puts "exit Plezi to observe the shutdown callbacks."}
|
402
405
|
|
403
406
|
## Re-write Routes
|
404
407
|
|
@@ -435,8 +438,6 @@ By using a route with the a 'false' controller, the parameters extracted are aut
|
|
435
438
|
end
|
436
439
|
end
|
437
440
|
|
438
|
-
listen
|
439
|
-
|
440
441
|
# this is our re-write route.
|
441
442
|
# it will extract the locale and re-write the request.
|
442
443
|
route '/:locale{fr|en}/*', false
|
@@ -500,8 +501,6 @@ and and other OAuth2 authentication service. For example:
|
|
500
501
|
scope: "public_profile,email") if ENV['FB_APP_ID'] && ENV['FB_APP_SECRET']
|
501
502
|
|
502
503
|
|
503
|
-
listen
|
504
|
-
|
505
504
|
create_auth_shared_route do |service_name, token, remote_user_id, remote_user_email, remote_response|
|
506
505
|
# we will create a temporary cookie storing a login message. replace this code with your app's logic
|
507
506
|
flash[:login] = "#{remote_response['name']} (#{remote_user_email}) from #{service_name}"
|
data/docs/async_helpers.md
CHANGED
@@ -2,19 +2,19 @@
|
|
2
2
|
|
3
3
|
(todo: write documentation)
|
4
4
|
|
5
|
-
Inside Plezi's core code is a pure Ruby IO reactor called [
|
5
|
+
Inside Plezi's core code is a pure Ruby IO reactor called [Iodine](https://github.com/boazsegev/iodine), a wonderful Asynchronous Workflow Engine that allows us to enjoy both Multi-Threading and Multi-Processing.
|
6
6
|
|
7
|
-
Although multi-threading is highly regarded, it should be pointed out that using the
|
7
|
+
Although multi-threading is highly regarded, it should be pointed out that using the [Iodine](https://github.com/boazsegev/iodine) with just one thread is both faster and more efficient. But, since some tasks that take more time (blocking tasks) can't be broken down into smaller tasks, using a number of threads (and/or processes) is a better practice.
|
8
8
|
|
9
|
-
You can read more about
|
9
|
+
You can read more about [Iodine](https://github.com/boazsegev/iodine) and it's amazing features in it's [documentation](http://www.rubydoc.info/github/boazsegev/iodine/master).
|
10
10
|
|
11
11
|
Here we will discuss the methods used for asynchronous processing of different tasks that allow us to break big heavy tasks into smaller bits, allowing our application to 'flow' and stay responsive even while under heavy loads.
|
12
12
|
|
13
13
|
## Asynchronous HTTP responses
|
14
14
|
|
15
|
-
Inside Plezi's core code is a pure Ruby
|
15
|
+
Inside Plezi's core code is a pure Ruby Http and Websocket Server (and client) that comes with [Iodine](https://github.com/boazsegev/iodine) and allows for native Http/1.1 streaming using `chunked` encoding or native Http/2 streaming (built-in to the protocol).
|
16
16
|
|
17
|
-
Asynchronous
|
17
|
+
Asynchronous Http method calls can be nested, but shouldn't be called one after the other.
|
18
18
|
|
19
19
|
i.e.:
|
20
20
|
|
@@ -29,9 +29,9 @@ response.stream_async { "I don't know..." }
|
|
29
29
|
Since streaming is done asynchronously, and since Plezi is multi-threaded by default (this can be changed to single threaded, but is less recomended unless you know your code doesn't block - see `Plezi::Settings.max_threads = number`), Asynchronous HTTP method nesting makes sure that the code doesn't conflict and that race conditions don't occure within the same HTTP response.
|
30
30
|
|
31
31
|
|
32
|
-
####
|
32
|
+
#### Iodines's `response.stream_async &block`
|
33
33
|
|
34
|
-
|
34
|
+
Iodines's response object, which is accessed by the controller using the `response` method (or the `@response` object), allows easy access to HTTP streaming.
|
35
35
|
|
36
36
|
For example (run this in the terminal using `irb`):
|
37
37
|
|
@@ -49,7 +49,6 @@ class MyController
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
-
listen
|
53
52
|
route '/', MyController
|
54
53
|
|
55
54
|
exit
|
@@ -59,14 +58,14 @@ As noted above, `response.stream_async` calls should always be nested and never
|
|
59
58
|
|
60
59
|
Calling `response.stream_async`
|
61
60
|
|
62
|
-
####
|
61
|
+
#### Iodines's `response.stream_array enum, &block`
|
63
62
|
|
64
|
-
To make nesting easier,
|
63
|
+
To make nesting easier, Iodines's response object provides the `response.stream_array enum, &block` method.
|
65
64
|
|
66
65
|
Here's our modified example:
|
67
66
|
|
68
67
|
```ruby
|
69
|
-
require
|
68
|
+
require 'plezi'
|
70
69
|
|
71
70
|
class MyController
|
72
71
|
def index
|
@@ -75,7 +74,6 @@ class MyController
|
|
75
74
|
end
|
76
75
|
end
|
77
76
|
|
78
|
-
listen
|
79
77
|
route '/', MyController
|
80
78
|
|
81
79
|
exit
|
@@ -84,7 +82,7 @@ exit
|
|
84
82
|
You can also add data to the array while 'looping', which allows you to use the array as a 'flag' for looped streaming. The following is a very limited example, which could be used for "lazy loading" data from a database, in order to save on system resources or send large table data using JSON "packets".
|
85
83
|
|
86
84
|
```ruby
|
87
|
-
require
|
85
|
+
require 'plezi'
|
88
86
|
|
89
87
|
class MyController
|
90
88
|
def index
|
@@ -97,7 +95,6 @@ class MyController
|
|
97
95
|
end
|
98
96
|
end
|
99
97
|
|
100
|
-
listen
|
101
98
|
route '/', MyController
|
102
99
|
|
103
100
|
exit
|
@@ -105,91 +102,144 @@ exit
|
|
105
102
|
|
106
103
|
## Asynchronous code execution
|
107
104
|
|
108
|
-
[
|
105
|
+
[Iodine](https://github.com/boazsegev/iodine) has a very powerful Asynchronous Workflow Engine which offers a very intuitve and simplified way to use API both for queuing code snippets (blocks / methods) and for schedualing non-persistent timed events (future timed events are discarded during shutdown and need to be re-initiated).
|
109
106
|
|
110
107
|
### The Asynchronous "Queue"
|
111
108
|
|
112
|
-
`
|
109
|
+
`Iodine`'s core is built with simplicity in mind, making the programmer happy. With this in mind, Iodine offers a single and simple method that allow us to easily queue code execution.
|
113
110
|
|
114
111
|
|
115
|
-
#### `
|
112
|
+
#### `Iodine.run(arg1, arg2, arg3...) {block}`
|
116
113
|
|
117
|
-
`
|
114
|
+
`Iodine.run` takes arguments to be passed to a block of code prior to execution. This allows us to seperate the `Proc` object creation fron the data handling and possibly (but not always) optimize our code.
|
118
115
|
|
119
116
|
For example:
|
120
117
|
|
121
118
|
```ruby
|
122
|
-
require
|
119
|
+
require 'plezi'
|
123
120
|
|
124
121
|
class MyController
|
125
122
|
def index
|
126
|
-
|
123
|
+
# Plezi.run automatically defers to Iodine.run
|
124
|
+
Plezi.run(Time.now) {|t| puts "Someone poked me at: #{t}" } # maybe send an email?
|
127
125
|
"Hello World"
|
128
126
|
end
|
129
127
|
end
|
130
128
|
|
131
|
-
|
129
|
+
|
132
130
|
route '/', MyController
|
133
131
|
|
134
132
|
exit
|
135
133
|
```
|
136
134
|
|
137
|
-
|
138
|
-
|
139
|
-
Another common method imployed is the `GR.callback`, which allows us to layer asynchronous code:
|
135
|
+
This might be optimized like so:
|
140
136
|
|
141
137
|
```ruby
|
142
|
-
require
|
138
|
+
require 'plezi'
|
143
139
|
|
144
140
|
class MyController
|
145
141
|
def index
|
146
|
-
|
142
|
+
Iodine.run Time.now, &self.class.action_proc
|
147
143
|
"Hello World"
|
148
144
|
end
|
145
|
+
|
149
146
|
protected
|
150
|
-
|
151
|
-
|
147
|
+
|
148
|
+
def self.action_proc
|
149
|
+
@proc ||= Proc.new {|t| puts "Someone poked me at: #{t}" } # maybe send an email?
|
152
150
|
end
|
153
151
|
end
|
154
152
|
|
155
|
-
listen
|
156
153
|
route '/', MyController
|
157
154
|
|
158
155
|
exit
|
159
156
|
```
|
160
157
|
|
161
|
-
|
158
|
+
### Timed events
|
162
159
|
|
163
|
-
|
160
|
+
Sometimes we want to schedule something to be done in a while, ormaybe we want a task to repeat every certain intevral...
|
164
161
|
|
165
|
-
|
166
|
-
2. An array of arguments to be passed to that object (or `nil`).
|
162
|
+
In come Iodine's TimedEvents: `Iodine.run_after` and `Iodine.run_every`
|
167
163
|
|
168
|
-
|
164
|
+
#### `Iodine.run_every(seconds, arg1, arg2...) {|arg1, arg2..., timed_event| block }`
|
169
165
|
|
170
|
-
|
166
|
+
`Iodine.run_every` is very similar to the `Iodine.run`, except it automatically injects an argument to our block, the timed even itself, as the last (optional) parameter. This allows us to stop the repeating event should the need arise.
|
171
167
|
|
172
168
|
```ruby
|
173
|
-
require
|
169
|
+
require 'plezi'
|
174
170
|
|
175
171
|
class MyController
|
172
|
+
def index
|
173
|
+
counter = 0
|
174
|
+
# Plezi.run_every automatically defers to Iodine.run_every
|
175
|
+
Plezi.run_every(1, "Counting: %i") do |str, timer|
|
176
|
+
counter +=1
|
177
|
+
puts(str % counter)
|
178
|
+
timer.stop! if counter >= 3
|
179
|
+
end
|
180
|
+
"Hello World"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
|
185
|
+
route '/', MyController
|
186
|
+
|
187
|
+
exit
|
188
|
+
```
|
189
|
+
|
190
|
+
A similar approach could have been accomplished by setting a repeat limit:
|
176
191
|
|
177
|
-
|
192
|
+
```ruby
|
193
|
+
require 'plezi'
|
178
194
|
|
195
|
+
class MyController
|
179
196
|
def index
|
180
|
-
|
197
|
+
timer = Iodine.run_every(1, "Counting down: %i") do |str, timer|
|
198
|
+
puts(str % timer.repeat_limit)
|
199
|
+
end
|
200
|
+
timer.repeat_limit = 3
|
181
201
|
"Hello World"
|
182
202
|
end
|
183
203
|
end
|
184
204
|
|
185
|
-
|
205
|
+
|
186
206
|
route '/', MyController
|
187
207
|
|
188
208
|
exit
|
189
209
|
```
|
190
210
|
|
211
|
+
#### `Iodine.run_after(seconds, arg1, arg2...) {|arg1, arg2..., timed_event| block}`
|
191
212
|
|
192
|
-
|
213
|
+
`Iodine.run_after` is very similar to the `Iodine.run_every`, except it is designed to "self destruct" after only only execution.
|
214
|
+
|
215
|
+
The timed event allows us to create a new event with the same job, if we wish to.
|
216
|
+
|
217
|
+
```ruby
|
218
|
+
require 'plezi'
|
219
|
+
|
220
|
+
class MyController
|
221
|
+
def index
|
222
|
+
counter = 0
|
223
|
+
# Plezi.run_after automatically defers to Iodine.run_after
|
224
|
+
Iodine.run_after(1, "Counting: %i") do |str, timer|
|
225
|
+
counter +=1
|
226
|
+
puts(str % counter)
|
227
|
+
Iodine.run_after(1, str, &timer.job) if counter < 3
|
228
|
+
end
|
229
|
+
"Hello World"
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
|
234
|
+
route '/', MyController
|
235
|
+
|
236
|
+
exit
|
237
|
+
```
|
238
|
+
|
239
|
+
/// To Do: complete doc.
|
193
240
|
|
194
241
|
## The Graceful Shutdown
|
195
242
|
|
243
|
+
Enter `Iodine.on_shutdown` - a reverse shutdown task queue (LIFO).
|
244
|
+
|
245
|
+
/// To Do: complete doc.
|