climax 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.markdown CHANGED
@@ -108,10 +108,12 @@ The above example is a simple application that adds an extra command line option
108
108
  `configure` method. The `pre_main` method is simple and just writes a debug log statement. As you
109
109
  can see logging has been setup at this point. Then the `main` method is called. Because the `main`
110
110
  method in this example always returns nil, this application will run forever until it is stopped
111
- externally (`Ctrl-C`, `kill -9`, or using the Control DRb). Using `Ctrl-C` and `kill -9` will
112
- immediately halt execution of the application and `post_main` will never be called. However if the
113
- Control DRb is used then the application will exit in an orderly fashion and the `post_main` method
114
- will be called. Notice that for free you can fork this application, change the log level with the
111
+ externally (`Ctrl-C`, `kill`, or using the Control DRb). Using `Ctrl-C` and `kill -INT` (i.e.,
112
+ sending an Interrupt signal) will cause the application to exit gracefully. The current iteration of
113
+ `main` will finish and then `post_main` will be run. You can send another Interrupt signal, for
114
+ example by hitting `Ctrl-C` twice, to exit the application immediately. Likewise if the Control DRb
115
+ is used then the application will exit in an orderly fashion and the `post_main` method will be
116
+ called. Notice that for free you can fork this application, change the log level with the
115
117
  `--log-level` option, write the logs to a file using the `--log-file` option, and you can start a
116
118
  debugger at any time while this application is running using the Control DRb. The Control DRb has a
117
119
  lot of power. We'll talk about it more below.
@@ -159,12 +161,19 @@ good idea to keep each iteration of `main` as short as possible, although this i
159
161
  requirement. In other words, if you wish to enter the remote debugger for a running process, the
160
162
  debugger will not begin until the current iteration of `main` has completed.
161
163
 
164
+ This is exactly how graceful exiting is accomplished with climax. When you send an Interrupt signal
165
+ (via `Ctrl-C` or by sending a `kill -INT` to your application), climax intercepts this signal and
166
+ places a `:exit` event onto the event queue. If climax intercepts a second Interrupt signal it will
167
+ halt execution immediately.
168
+
162
169
  This is excellent for stopping a long running process without interrupting its work. By sending an
163
- :exit or :quit event, whether through the Control DRb or from your application itself, your
164
- application will be allowed to finish processing its current unit of work before exiting.
170
+ :exit or :quit event, whether through the Control DRb, or by sending an Interrupt signal, or from
171
+ your application itself, your application will be allowed to finish processing its current unit of
172
+ work before exiting.
165
173
 
166
174
  For your convenience there is also a 'climax_has_event?' method that returns true only if there are
167
- events waiting on the event queue.
175
+ events waiting on the event queue. Your application can use this to determine if `main` should give
176
+ up control temporarily to allow climax to handle events on the queue.
168
177
 
169
178
  Generating a New Application
170
179
  ============================
@@ -237,3 +246,104 @@ and then execute:
237
246
  climax control start_debugger
238
247
 
239
248
  When you are finished type `quit` to exit the debugger and resume your application.
249
+
250
+ Extending the Control DRb with Custom Functionality
251
+ ---------------------------------------------------
252
+
253
+ Extending the Control DRb is a key aspect of climax. For instance it is a common idiom for web
254
+ applications to delegate difficult tasks to background jobs. Let's take a common scenario and see
255
+ how we might implement this scenario with climax.
256
+
257
+ In this example there is a web application that allows users to upload images. The image is stored
258
+ in a temporary directory by the web application and then a request is sent to a background job
259
+ asking it to process the image and store it in a more permanent location. Let's say the web
260
+ application simply wants to send the command `process_image` and pass as an argument the path of the
261
+ image that was uploaded.
262
+
263
+ Let's see how we can extend the Control DRb with a `process_image` method. We'll also see a possible
264
+ workflow for how the background job might tackle its work.
265
+
266
+ require 'climax'
267
+ require 'fileutils'
268
+
269
+ module Climax
270
+ class ControlServer
271
+ def process_image (path)
272
+ app.climax_send_event(:process_image, path)
273
+ end
274
+ end
275
+ end
276
+
277
+ class ImageProcessor
278
+ include Climax::Application
279
+
280
+ def configure
281
+ options do
282
+ on 't', 'target-directory=', 'Destination directory to save processed images to.', :default => '/var/saved/images'
283
+ end
284
+ end
285
+
286
+ def pre_main
287
+ FileUtils.mkdir_p(opts[:'target-directory'])
288
+ @processor_queue = []
289
+ end
290
+
291
+ def main
292
+ if @processor_queue.empty?
293
+ sleep 0.5
294
+ return nil
295
+ end
296
+
297
+ image_path = @processor_queue.pop
298
+ log.info "Processing image #{image_path}"
299
+ do_work(image_path)
300
+ return nil
301
+ end
302
+
303
+ def process_image (path)
304
+ @processor_queue.push(path)
305
+ end
306
+
307
+ def do_work(path)
308
+ # process image at path
309
+ # save to target directory
310
+ end
311
+ end
312
+
313
+ Adding your own commands to the Control DRb couldn't be easier. Simply extend the
314
+ `Climax::ControlServer` class with your own methods that push events onto the climax event queue.
315
+ When climax processes the event it will call a method in your application instance with the same
316
+ name as the event. In the example above we send a `:process_image` event with an argument. When
317
+ climax processes this event it will attempt to call a `process_image` method in the application
318
+ instance, passing along any arguments.
319
+
320
+ In this case we simply add the work to a simple queue and allow `main` to process the jobs from the
321
+ queue.
322
+
323
+ Notice that when you are extending `Climax::ControlServer` you have access to your application
324
+ instance via the `app` method. From here you can call any publicly available methods on your
325
+ application instance. *But be careful*. The DRb is running in a separate thread. This is why we
326
+ simply send events! It is always best to only send events from your custom ControlServer methods.
327
+ Doing so allows you to never need worry about making your application thread-safe. If you're a
328
+ cowboy and decide to call various public methods on your app instance directly from your custom
329
+ `ControlServer` methods you may end up with some strange results. So unless you are familiar with
330
+ thread-safe applications it is suggested that you only place events onto the queue from your custom
331
+ `ControlServer` methods.
332
+
333
+ Now the web application can simply connect to your application's DRb and call the `process_image`
334
+ method, passing it an image path.
335
+
336
+ Something interesting about this is that non-ruby applications can also easily tell our image
337
+ processor to process images.
338
+
339
+ For instance you can send commands to your application using the `climax` CLI interface:
340
+
341
+ climax control process_image /tmp/uploaded-images/tmp-d8ej2.jpg
342
+
343
+ This means any application, even just simple shell scripts, can easily send commands to our
344
+ background job.
345
+
346
+ Of course it is best to give each of your climax applications a unique Control DRb port. You can
347
+ then specify which port to send commands to with the `-p` option to `climax control`:
348
+
349
+ climax control -p $IMAGE_PROCESSOR_PORT process_image /tmp/uploaded-images/tmp-d8ej2.jpg
@@ -172,6 +172,8 @@ module Climax
172
172
  when :stop_control_drb then DRb.stop_service
173
173
  when :start_remote_debugger then binding.remote_pry rescue nil
174
174
  when :quit, :exit then return 0
175
+ else
176
+ self.send(event.type, *[event.payload])
175
177
  end
176
178
  end
177
179
  end while !event.nil?
@@ -2,20 +2,23 @@ require 'drb/drb'
2
2
 
3
3
  module Climax
4
4
  class ControlServer
5
+
5
6
  def initialize(app)
6
7
  @app = app
7
8
  end
8
9
 
10
+ attr_reader :app
11
+
9
12
  def log_level
10
- @app.log_level
13
+ app.log_level
11
14
  end
12
15
 
13
16
  def log_level= (level)
14
- @app.climax_send_event(:set_log_level, level)
17
+ app.climax_send_event(:set_log_level, level)
15
18
  end
16
19
 
17
20
  def start_debugger
18
- @app.climax_send_event(:start_remote_debugger)
21
+ app.climax_send_event(:start_remote_debugger)
19
22
  end
20
23
  end
21
24
 
@@ -1,3 +1,3 @@
1
1
  module Climax
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: climax
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-25 00:00:00.000000000 Z
12
+ date: 2013-02-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pry