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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: debb56bb8606bda13bf432ea12c79154a2240bf2
4
- data.tar.gz: b7b030e87997f82457735b63f856a77b0d715c05
3
+ metadata.gz: c3641815744ffb10926c4e95a6fd47a7fcb4ba1f
4
+ data.tar.gz: a44e5be3f88040d8d46ff5adf6469fd3efad66c9
5
5
  SHA512:
6
- metadata.gz: a405525ff846eff0566a3c4e00d4949a3c93625a8cb5fd5801023c453c4f7b39d4764c70d5e2614b17e7849091d7a28b672c04f6c8dac4bb9a6337f06b7f7373
7
- data.tar.gz: 6a5510d3cbbfa8f99b007a3c197c201ca9751e286df4818727bf2d00ba5da74db2d86d84206c67e1a49b53db8ccca9b3e5bce5febf35bff82673e8828b3bca16
6
+ metadata.gz: 88072db2e3f71acb5a5b5e96764f285f20e7b8a0194e07122b24fd06e46fec9b38aa98c0a27a9c6551c3e30625b7568f8bb2765db752e20dd836da371c4bfe18
7
+ data.tar.gz: 77c52cfba2bfb6e2ad0c7c7a80dd0d2d84a8d0893327ccb983406b0b4e049fcbbf4134dbbdb22517ed46f19ac4d1b6093b9701c0ab6ad223cb537a31303a2da5
@@ -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 [GRHttp server's](https://github.com/boazsegev/GRHttp) 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).
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 first port for the app is 3000. you can set the first port to listen to by using the `-p ` option (make sure you have permissions for the requested port):
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
- listen
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 [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.
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 [GRHttp's](https://github.com/boazsegev/GRHttp) Rack adapter to run your Rack app, while Plezi will use GRHttp's native features (such as Websockets and HTTP streaming).
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::Placebo.new MyReciever
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 GRHttp serever. This Placebo connection will answer the Redis broadcasts just as if your class was a websocket controller...
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::Placebo.new MyReciever
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 [GReactor API](http://www.rubydoc.info/gems/greactor)'s.
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
- listen
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 GReactor methods, helping with logging as well as the support you already noticed for dynamic routes, dynamic services and more.
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
- GReactor.info 'This is the same, but more direct.'
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 GReactor API directly when using API that is delegated to GReactor.
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 [GReactor's API](http://www.rubydoc.info/gems/greactor/GReactor) to help with asynchronous tasking, callbacks, timers and customized shutdown cleanup.
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
- GReactor.on_shutdown(Kernel, :my_shutdown_proc, Time.now) { puts "this will run after shutdown." }
392
- GReactor.on_shutdown() { puts "this will run too." }
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
- GReactor.run_after 2, -> {puts "this will wait 2 seconds to run... too late. for this example"}
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
- GReactor.run_async {puts "notice that the background tasks will only start once the Plezi's engine is running."}
401
- GReactor.run_async {puts "exit Plezi to observe the shutdown callbacks."}
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}"
@@ -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 [GReactor](https://github.com/boazsegev/GReactor) (Generic Reactor), a very powerful Asynchronous Workflow Engine that allows us to enjoy both Multi-Threading and Multi-Processing.
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 GReactor 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.
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 the [GReactor](https://github.com/boazsegev/GReactor) and it's amazing features in it's [documentation](http://www.rubydoc.info/github/boazsegev/GReactor/master).
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 HTTP and Websocket Server (and client) called [GRHttp](https://github.com/boazsegev/GRHttp) (Generic HTTP), which allows for native HTTP streaming.
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 HTTP method calls can be nested, but shouldn't be called on after the other.
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
- #### GRHttp's `response.stream_async &block`
32
+ #### Iodines's `response.stream_async &block`
33
33
 
34
- GRHttp's response object, which is accessed by the controller using the `response` method (or the `@response` object), allows easy access to HTTP streaming.
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
- #### GRHttp's `response.stream_array enum, &block`
61
+ #### Iodines's `response.stream_array enum, &block`
63
62
 
64
- To make nesting easier, GRHttp's response object provides the `response.stream_array enum, &block` method.
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 `plezi`
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 `plezi`
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
- [GReactor](https://github.com/boazsegev/GReactor) (Generic Reactor), a very powerful Asynchronous Workflow Engine which offers a very intuitve and easy 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).
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
- `GReactor` (in short: `GR`) offers a number of methods that allow us to easily queue code execution.
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
- #### `GR.run_async(arg1, arg2, arg3...) {block}`
112
+ #### `Iodine.run(arg1, arg2, arg3...) {block}`
116
113
 
117
- `GR.run_async` 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.
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 `plezi`
119
+ require 'plezi'
123
120
 
124
121
  class MyController
125
122
  def index
126
- GR.run_async(Time.now) {|t| puts "Someone poked me at: #{t}"} # maybe send an email?
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
- listen
129
+
132
130
  route '/', MyController
133
131
 
134
132
  exit
135
133
  ```
136
134
 
137
- #### `GR.callback(object, method, arg1, arg2...) {|returned_value| callback block}`
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 `plezi`
138
+ require 'plezi'
143
139
 
144
140
  class MyController
145
141
  def index
146
- GR.callback(self, :print_poke, Time.now) { puts "Printed poke."}
142
+ Iodine.run Time.now, &self.class.action_proc
147
143
  "Hello World"
148
144
  end
145
+
149
146
  protected
150
- def print_poke time
151
- puts "Someone poked me at: #{time}"
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
- #### `GR.queue(proc, arguments_array)`
158
+ ### Timed events
162
159
 
163
- GReactor's asynchronous engine is, in it's core, based off this simple method which accepts two elements:
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
- 1. An object that answers to `call`
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
- This method allows us to easily reuse Proc objects without constantly creating new Proc objects and releasing them from memory.
164
+ #### `Iodine.run_every(seconds, arg1, arg2...) {|arg1, arg2..., timed_event| block }`
169
165
 
170
- For example:
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 `plezi`
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
- POKE_TASK = -> {|time| puts "Someone poked me at: #{time}"}
192
+ ```ruby
193
+ require 'plezi'
178
194
 
195
+ class MyController
179
196
  def index
180
- GR.queue POKE_TASK, [Time.now]
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
- listen
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
- ### Timed events
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.