bj 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY ADDED
@@ -0,0 +1,2 @@
1
+ 0.0.1:
2
+ - initial release
data/README ADDED
@@ -0,0 +1,130 @@
1
+ NAME
2
+ bj
3
+
4
+ SYNOPSIS
5
+ bj (migration_code|generate_migration|migrate|setup|run|submit|list|set|config|pid) [options]+
6
+
7
+ DESCRIPTION
8
+ ________________________________
9
+ Overview
10
+ --------------------------------
11
+
12
+ Backgroundjob (Bj) is a simple to use background priority queue for rails.
13
+ Although not yet tested on windows, the design of bj is such that operation
14
+ should be possible on any operating system, including M$.
15
+
16
+ Jobs can be submitted to the queue directly using the api or from the
17
+ commandline using the 'bj' script. For example
18
+
19
+ code:
20
+ Bj.submit 'cat /etc/password'
21
+
22
+ cli:
23
+ bj submit cat /etc/password
24
+
25
+ When used from inside a rails application bj arranges that another process
26
+ will always be running in the background to process the jobs that you submit.
27
+ By using a separate process to run jobs bj does not impact the resource
28
+ utilization of your rails application at all and enables several very cool
29
+ features:
30
+
31
+ 1) Bj allows you to sumbit jobs to any of your configured databases and,
32
+ in each case, spawns a separate background process to run jobs from that
33
+ queue
34
+
35
+ Bj.in :production do
36
+ Bj.submit 'production_job.exe'
37
+ end
38
+
39
+ Bj.in :development do
40
+ Bj.submit 'development_job.exe'
41
+ end
42
+
43
+ 2) Although bj ensures that a process is always running to process
44
+ your jobs, you can start a proces manually. This means that any machine
45
+ capable of seeing your RAILS_ROOT can run jobs for your application, allowing
46
+ one to setup a cluster of machines doing the work of a single front end rails
47
+ applicaiton.
48
+
49
+ ________________________________
50
+ Install
51
+ --------------------------------
52
+
53
+ Bj can be installed two ways: as a gem or as a plugin.
54
+
55
+ gem:
56
+
57
+ 1) $sudo gem install bj
58
+ 2) bj setup ./rails_root/
59
+
60
+ plugin:
61
+
62
+ 1) ./script/plugin install http://codeforpeople.rubyforge.org/svn/rails/plugins/bj
63
+ 2) ./script/bj setup
64
+
65
+ ________________________________
66
+ Api
67
+ --------------------------------
68
+
69
+ submit jobs for background processing. 'jobs' can be a string or array of
70
+ strings. options are applied to each job in the 'jobs', and the list of
71
+ submitted jobs is always returned. options (string or symbol) can be
72
+
73
+ :rails_env => production|development|key_in_database_yml
74
+ when given this keyword causes bj to submit jobs to the
75
+ specified database. default is RAILS_ENV.
76
+
77
+ :priority => any number, including negative ones. default is zero.
78
+
79
+ :tag => a tag added to the job. simply makes searching easier.
80
+
81
+ :env => a hash specifying any additional environment vars the background
82
+ process should have.
83
+
84
+ :stdin => any stdin the background process should have.
85
+
86
+ eg:
87
+
88
+ jobs = Bj.submit 'echo foobar', :tag => 'simple job'
89
+
90
+ jobs = Bj.submit '/bin/cat', :stdin => 'in the hat'
91
+
92
+ jobs = Bj.submit './script/runner ./scripts/a.rb', :rails_env => 'production'
93
+
94
+ when jobs are run, they are run in RAILS_ROOT. various attributes are
95
+ available *only* once the job has finished. you can check whether or not a
96
+ job is finished by using the #finished method, which simple does a reload and
97
+ checks to see if the exit_status is non-nil.
98
+
99
+ eg:
100
+
101
+ jobs = Bj.submit list_of_jobs, :tag => 'important'
102
+ ...
103
+
104
+ jobs.each do |job|
105
+ if job.finished?
106
+ p job.exit_status
107
+ p job.stdout
108
+ p job.stderr
109
+ end
110
+ end
111
+
112
+ See lib/bj/api.rb for more details.
113
+
114
+ PARAMETERS
115
+ --rails_root=rails_root, -R (0 ~> rails_root=)
116
+ the rails_root will be guessed unless you set this
117
+ --rails_env=rails_env, -E (0 ~> rails_env=development)
118
+ set the rails_env
119
+ --log=log, -l (0 ~> log=STDERR)
120
+ set the logfile
121
+ --help, -h
122
+
123
+ AUTHOR
124
+ ara.t.howard@gmail.com
125
+
126
+ URIS
127
+ http://codeforpeople.com/lib/ruby/
128
+ http://rubyforge.org/projects/codeforpeople/
129
+ http://codeforpeople.rubyforge.org/svn/rails/plugins/
130
+
data/TODO ADDED
@@ -0,0 +1,3 @@
1
+ * flesh out the cli interface - it's a test only at this point
2
+ * test in windows
3
+ * make sure database.yml is loaded via YAML::load(ERB.new(File.read * "config/database.yml").result)
data/bin/bj ADDED
@@ -0,0 +1,478 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require "bj"
4
+
5
+ Main {
6
+ usage["description"] = <<-txt
7
+ ________________________________
8
+ Overview
9
+ --------------------------------
10
+
11
+ Backgroundjob (Bj) is a simple to use background priority queue for rails.
12
+ Although not yet tested on windows, the design of bj is such that operation
13
+ should be possible on any operating system, including M$.
14
+
15
+ Jobs can be submitted to the queue directly using the api or from the
16
+ commandline using the 'bj' script. For example
17
+
18
+ code:
19
+ Bj.submit 'cat /etc/password'
20
+
21
+ cli:
22
+ bj submit cat /etc/password
23
+
24
+ When used from inside a rails application bj arranges that another process
25
+ will always be running in the background to process the jobs that you submit.
26
+ By using a separate process to run jobs bj does not impact the resource
27
+ utilization of your rails application at all and enables several very cool
28
+ features:
29
+
30
+ 1) Bj allows you to sumbit jobs to any of your configured databases and,
31
+ in each case, spawns a separate background process to run jobs from that
32
+ queue
33
+
34
+ Bj.in :production do
35
+ Bj.submit 'production_job.exe'
36
+ end
37
+
38
+ Bj.in :development do
39
+ Bj.submit 'development_job.exe'
40
+ end
41
+
42
+ 2) Although bj ensures that a process is always running to process
43
+ your jobs, you can start a proces manually. This means that any machine
44
+ capable of seeing your RAILS_ROOT can run jobs for your application, allowing
45
+ one to setup a cluster of machines doing the work of a single front end rails
46
+ applicaiton.
47
+
48
+ ________________________________
49
+ Install
50
+ --------------------------------
51
+
52
+ Bj can be installed two ways: as a gem or as a plugin.
53
+
54
+ gem:
55
+
56
+ 1) $sudo gem install bj
57
+ 2) bj setup ./rails_root/
58
+
59
+ plugin:
60
+
61
+ 1) ./script/plugin install http://codeforpeople.rubyforge.org/svn/rails/plugins/bj
62
+ 2) ./script/bj setup
63
+
64
+ ________________________________
65
+ Api
66
+ --------------------------------
67
+
68
+ submit jobs for background processing. 'jobs' can be a string or array of
69
+ strings. options are applied to each job in the 'jobs', and the list of
70
+ submitted jobs is always returned. options (string or symbol) can be
71
+
72
+ :rails_env => production|development|key_in_database_yml
73
+ when given this keyword causes bj to submit jobs to the
74
+ specified database. default is RAILS_ENV.
75
+
76
+ :priority => any number, including negative ones. default is zero.
77
+
78
+ :tag => a tag added to the job. simply makes searching easier.
79
+
80
+ :env => a hash specifying any additional environment vars the background
81
+ process should have.
82
+
83
+ :stdin => any stdin the background process should have.
84
+
85
+ eg:
86
+
87
+ jobs = Bj.submit 'echo foobar', :tag => 'simple job'
88
+
89
+ jobs = Bj.submit '/bin/cat', :stdin => 'in the hat'
90
+
91
+ jobs = Bj.submit './script/runner ./scripts/a.rb', :rails_env => 'production'
92
+
93
+ when jobs are run, they are run in RAILS_ROOT. various attributes are
94
+ available *only* once the job has finished. you can check whether or not a
95
+ job is finished by using the #finished method, which simple does a reload and
96
+ checks to see if the exit_status is non-nil.
97
+
98
+ eg:
99
+
100
+ jobs = Bj.submit list_of_jobs, :tag => 'important'
101
+ ...
102
+
103
+ jobs.each do |job|
104
+ if job.finished?
105
+ p job.exit_status
106
+ p job.stdout
107
+ p job.stderr
108
+ end
109
+ end
110
+
111
+ See lib/bj/api.rb for more details.
112
+
113
+ ________________________________
114
+ Sponsors
115
+ --------------------------------
116
+ http://www.engineyard.com/
117
+ http://quintess.com/
118
+ http://eparklabs.com/
119
+ txt
120
+
121
+ usage["uris"] = <<-txt
122
+ http://codeforpeople.com/lib/ruby/
123
+ http://rubyforge.org/projects/codeforpeople/
124
+ http://codeforpeople.rubyforge.org/svn/rails/plugins/
125
+ txt
126
+
127
+ author "ara.t.howard@gmail.com"
128
+
129
+ option("rails_root", "R"){
130
+ description "the rails_root will be guessed unless you set this"
131
+ argument_required
132
+ default RAILS_ROOT
133
+ }
134
+
135
+ option("rails_env", "E"){
136
+ description "set the rails_env"
137
+ argument_required
138
+ default RAILS_ENV
139
+ }
140
+
141
+ option("log", "l"){
142
+ description "set the logfile"
143
+ argument_required
144
+ default STDERR
145
+ }
146
+
147
+
148
+ mode "migration_code" do
149
+ description "dump migration code on stdout"
150
+
151
+ def run
152
+ puts Bj.table.migration_code
153
+ end
154
+ end
155
+
156
+ mode "generate_migration" do
157
+ description "generate a migration"
158
+
159
+ def run
160
+ Bj.generate_migration
161
+ end
162
+ end
163
+
164
+ mode "migrate" do
165
+ description "migrate the db"
166
+
167
+ def run
168
+ Bj.migrate
169
+ end
170
+ end
171
+
172
+ mode "setup" do
173
+ description "generate a migration and migrate"
174
+
175
+ def run
176
+ Bj.setup
177
+ end
178
+ end
179
+
180
+ mode "run" do
181
+ description "start a job runnner, possibly as a daemon"
182
+
183
+ option("--forever"){}
184
+ option("--daemon"){}
185
+ option("--ppid"){
186
+ argument :required
187
+ cast :integer
188
+ }
189
+ option("--redirect"){
190
+ argument :required
191
+ }
192
+
193
+ def run
194
+ options = {}
195
+
196
+ %w[ forever daemon ppid ].each do |key|
197
+ options[key.to_sym] = true if param[key].given?
198
+ end
199
+
200
+ if param["redirect"].given?
201
+ open(param["redirect"].value, "a+") do |fd|
202
+ STDERR.reopen fd
203
+ STDOUT.reopen fd
204
+ end
205
+ STDERR.sync = true
206
+ STDOUT.sync = true
207
+ end
208
+
209
+ trap("SIGTERM"){
210
+ info{ "SIGTERM" }
211
+ exit
212
+ }
213
+
214
+ if param["daemon"].given?
215
+ daemon{ Bj.run options }
216
+ else
217
+ Bj.run options
218
+ end
219
+ end
220
+ end
221
+
222
+ mode "submit" do
223
+ keyword("file"){
224
+ argument :required
225
+ attr
226
+ }
227
+
228
+ def run
229
+ joblist = Bj.joblist.for argv.join(' ')
230
+
231
+ case file
232
+ when "-"
233
+ joblist.push(Bj.joblist.jobs_from_io(STDIN))
234
+ when "--", "---"
235
+ joblist.push(Bj.joblist.jobs_from_yaml(STDIN))
236
+ else
237
+ open(file){|io| joblist.push(Bj.joblist.jobs_from_io(io)) }
238
+ end
239
+
240
+ jobs = Bj.submit joblist, :no_tickle => true
241
+
242
+ oh = lambda{|job| OrderedHash["id", job.id, "command", job.command]}
243
+
244
+ y jobs.map{|job| oh[job]}
245
+ end
246
+ end
247
+
248
+ mode "list" do
249
+ def run
250
+ Bj.transaction do
251
+ y Bj::Table::Job.find(:all).map(&:to_hash)
252
+ end
253
+ end
254
+ end
255
+
256
+ mode "set" do
257
+ argument("key"){ attr }
258
+
259
+ argument("value"){ attr }
260
+
261
+ option("hostname", "H"){
262
+ argument :required
263
+ default Bj.hostname
264
+ attr
265
+ }
266
+
267
+ option("cast", "c"){
268
+ argument :required
269
+ default "to_s"
270
+ attr
271
+ }
272
+
273
+ def run
274
+ Bj.transaction do
275
+ Bj.config.set(key, value, :hostname => hostname, :cast => cast)
276
+ y Bj.table.config.for(:hostname => hostname)
277
+ end
278
+ end
279
+ end
280
+
281
+ mode "config" do
282
+ option("hostname", "H"){
283
+ argument :required
284
+ default Bj.hostname
285
+ }
286
+
287
+ def run
288
+ Bj.transaction do
289
+ y Bj.table.config.for(:hostname => param["hostname"].value)
290
+ end
291
+ end
292
+ end
293
+
294
+ mode "pid" do
295
+ option("hostname", "H"){
296
+ argument :required
297
+ default Bj.hostname
298
+ }
299
+
300
+ def run
301
+ Bj.transaction do
302
+ config = Bj.table.config.for(:hostname => param["hostname"].value)
303
+ puts config[ "#{ RAILS_ENV }.pid" ] if config
304
+ end
305
+ end
306
+ end
307
+
308
+
309
+ def run
310
+ help!
311
+ end
312
+
313
+ def before_run
314
+ self.logger = param["log"].value
315
+ Bj.logger = logger
316
+
317
+ if param["rails_root"].given?
318
+ rails_root = param["rails_root"].value
319
+ ENV["RAILS_ROOT"] = rails_root
320
+ ::Object.instance_eval do
321
+ remove_const :RAILS_ROOT
322
+ const_set :RAILS_ROOT, rails_root
323
+ end
324
+ end
325
+
326
+ if param["rails_env"].given?
327
+ rails_env = param["rails_env"].value
328
+ ENV["RAILS_ENV"] = rails_env
329
+ ::Object.instance_eval do
330
+ remove_const :RAILS_ENV
331
+ const_set :RAILS_ENV, rails_env
332
+ end
333
+ end
334
+ end
335
+
336
+ def daemon
337
+ ra, wa = IO.pipe
338
+ rb, wb = IO.pipe
339
+ if fork
340
+ at_exit{ exit! }
341
+ wa.close
342
+ r = ra
343
+ rb.close
344
+ w = wb
345
+ pid = r.gets
346
+ w.puts pid
347
+ Integer pid.strip
348
+ else
349
+ ra.close
350
+ w = wa
351
+ wb.close
352
+ r = rb
353
+ open("/dev/null", "r+") do |fd|
354
+ STDIN.reopen fd
355
+ STDOUT.reopen fd
356
+ STDERR.reopen fd
357
+ end
358
+ Process::setsid rescue nil
359
+ pid =
360
+ fork do
361
+ Dir::chdir RAILS_ROOT
362
+ File::umask 0
363
+ $DAEMON = true
364
+ yield
365
+ exit!
366
+ end
367
+ w.puts pid
368
+ r.gets
369
+ exit!
370
+ end
371
+ end
372
+ }
373
+
374
+
375
+
376
+
377
+
378
+ #
379
+ # we setup a few things so the script works regardless of whether it was
380
+ # called out of /usr/local/bin, ./script, or wherever. note that the script
381
+ # does *not* require the entire rails application to be loaded into memory!
382
+ # we could just load boot.rb and environment.rb, but this method let's
383
+ # submitting and running jobs be infinitely more lightweight.
384
+ #
385
+
386
+ BEGIN {
387
+ #
388
+ # see if we're running out of RAILS_ROOT/script/
389
+ #
390
+ unless defined? BJ_IS_SCRIPT #{{{
391
+ BJ_IS_SCRIPT =
392
+ if %w[ script config app ].map{|d| test ?d, "#{ File.dirname __FILE__ }/../#{ d }"}.all?
393
+ __FILE__
394
+ else
395
+ nil
396
+ end
397
+ end #}}}
398
+
399
+ #
400
+ # setup RAILS_ROOT
401
+ #
402
+ unless defined? RAILS_ROOT #{{{
403
+ ### grab env var first
404
+ rails_root = ENV["RAILS_ROOT"]
405
+
406
+ ### commandline usage clobbers
407
+ kv = nil
408
+ ARGV.delete_if{|arg| arg =~ %r/^RAILS_ROOT=/ and kv = arg}
409
+ rails_root = kv.split(%r/=/,2).last if kv
410
+
411
+ ### we know the rails_root if we are in RAILS_ROOT/script/
412
+ unless rails_root
413
+ if BJ_IS_SCRIPT
414
+ rails_root = File.expand_path "#{ File.dirname __FILE__ }/.."
415
+ end
416
+ end
417
+
418
+ ### perhaps the current directory is a rails_root?
419
+ unless rails_root
420
+ if %w[ script config app ].map{|d| test(?d, d)}.all?
421
+ rails_root = File.expand_path "."
422
+ end
423
+ end
424
+
425
+ ### bootstrap
426
+ RAILS_ROOT = rails_root
427
+ end #}}}
428
+
429
+ #
430
+ # setup RAILS_ENV
431
+ #
432
+ unless defined? RAILS_ENV #{{{
433
+ ### grab env var first
434
+ rails_env = ENV["RAILS_ENV"]
435
+
436
+ ### commandline usage clobbers
437
+ kv = nil
438
+ ARGV.delete_if{|arg| arg =~ %r/^RAILS_ENV=/ and kv = arg}
439
+ rails_env = kv.split(%r/=/,2).last if kv
440
+
441
+ ### fallback to development
442
+ unless rails_env
443
+ rails_env = "development"
444
+ end
445
+
446
+ ### bootstrap
447
+ RAILS_ENV = rails_env
448
+ end #}}}
449
+
450
+ #
451
+ # setup $LOAD_PATH to detect plugin - iff it is installed and running out of
452
+ # RAILS_ROOT/script
453
+ #
454
+ if RAILS_ROOT #{{{
455
+ if BJ_IS_SCRIPT and test(?d, "#{ RAILS_ROOT }/vendor/plugins/bj/lib")
456
+ $LOAD_PATH.unshift "#{ RAILS_ROOT }/vendor/plugins/bj/lib"
457
+ end
458
+ end #}}}
459
+
460
+ #
461
+ # ensure that rubygems is loaded
462
+ #
463
+ begin
464
+ require "rubygems"
465
+ rescue
466
+ 42
467
+ end
468
+
469
+ #
470
+ # hack to_s of STDERR/STDOUT for nice help messages
471
+ #
472
+ class << STDERR
473
+ def to_s() 'STDERR' end
474
+ end
475
+ class << STDOUT
476
+ def to_s() 'STDOUT' end
477
+ end
478
+ }