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 +117 -7
- data/lib/climax/application.rb +2 -0
- data/lib/climax/control_drb.rb +6 -3
- data/lib/climax/version.rb +1 -1
- metadata +2 -2
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
|
112
|
-
|
113
|
-
|
114
|
-
|
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
|
164
|
-
application will be allowed to finish processing its current unit of
|
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
|
data/lib/climax/application.rb
CHANGED
@@ -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?
|
data/lib/climax/control_drb.rb
CHANGED
@@ -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
|
-
|
13
|
+
app.log_level
|
11
14
|
end
|
12
15
|
|
13
16
|
def log_level= (level)
|
14
|
-
|
17
|
+
app.climax_send_event(:set_log_level, level)
|
15
18
|
end
|
16
19
|
|
17
20
|
def start_debugger
|
18
|
-
|
21
|
+
app.climax_send_event(:start_remote_debugger)
|
19
22
|
end
|
20
23
|
end
|
21
24
|
|
data/lib/climax/version.rb
CHANGED
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.
|
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-
|
12
|
+
date: 2013-02-26 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: pry
|