climax 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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