ThiagoLelis-backgroundjob 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY CHANGED
@@ -1,9 +1,9 @@
1
- 2009-01-30:
2
- - fixed find(:first, :lock => true) issue with oracle databases
3
- - converted clob (:text) columns to varchar (:string) when creating Bj tables (a bug in the oracle-adapter prevented the clobs from saving properly)
4
-
5
- 2008-09-12:
6
- - brought the plugin up to 1.0.4 with the new utc option (Brandon Arbini)
7
-
8
- 2007-12-18T14:19:22.86-07:00
9
- - corrected install libs
1
+ 2009-01-30:
2
+ - fixed find(:first, :lock => true) issue with oracle databases
3
+ - converted clob (:text) columns to varchar (:string) when creating Bj tables (a bug in the oracle-adapter prevented the clobs from saving properly)
4
+
5
+ 2008-09-12:
6
+ - brought the plugin up to 1.0.4 with the new utc option (Brandon Arbini)
7
+
8
+ 2007-12-18T14:19:22.86-07:00
9
+ - corrected install libs
data/README CHANGED
@@ -1,142 +1,144 @@
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
- 1) $sudo gem install bj
57
- 2) add "require 'bj'" to config/environment.rb
58
- 3) bj setup
59
-
60
- plugin:
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', :priority => 42
90
-
91
- jobs = Bj.submit './script/runner ./scripts/a.rb', :rails_env => 'production'
92
-
93
- jobs = Bj.submit './script/runner /dev/stdin',
94
- :stdin => 'p RAILS_ENV',
95
- :tag => 'dynamic ruby code'
96
-
97
- jobs Bj.submit array_of_commands, :priority => 451
98
-
99
- when jobs are run, they are run in RAILS_ROOT. various attributes are
100
- available *only* once the job has finished. you can check whether or not a
101
- job is finished by using the #finished method, which simple does a reload and
102
- checks to see if the exit_status is non-nil.
103
-
104
- eg:
105
-
106
- jobs = Bj.submit list_of_jobs, :tag => 'important'
107
- ...
108
-
109
- jobs.each do |job|
110
- if job.finished?
111
- p job.exit_status
112
- p job.stdout
113
- p job.stderr
114
- end
115
- end
116
-
117
- See lib/bj/api.rb for more details.
118
-
119
- ________________________________
120
- Sponsors
121
- --------------------------------
122
- http://www.engineyard.com/
123
- http://quintess.com/
124
- http://eparklabs.com/
125
-
126
- PARAMETERS
127
- --rails_root=rails_root, -R (0 ~> rails_root=)
128
- the rails_root will be guessed unless you set this
129
- --rails_env=rails_env, -E (0 ~> rails_env=development)
130
- set the rails_env
131
- --log=log, -l (0 ~> log=STDERR)
132
- set the logfile
133
- --help, -h
134
-
135
- AUTHOR
136
- ara.t.howard@gmail.com
137
-
138
- URIS
139
- http://codeforpeople.com/lib/ruby/
140
- http://rubyforge.org/projects/codeforpeople/
141
- http://codeforpeople.rubyforge.org/svn/rails/plugins/
142
-
1
+ NAME
2
+ <<<<<<< HEAD:README
3
+ bj
4
+ =======
5
+ BackgroundJob
6
+ >>>>>>> 447a583065039f1493ece0fbdde813adab37f842:README
7
+
8
+ SYNOPSIS
9
+ bj (migration_code|generate_migration|migrate|setup|run|submit|list|set|config|pid) [options]+
10
+
11
+ DESCRIPTION
12
+ ________________________________
13
+ Overview
14
+ --------------------------------
15
+
16
+ Backgroundjob (Bj) is a simple to use background priority queue for rails.
17
+ Although not yet tested on windows, the design of bj is such that operation
18
+ should be possible on any operating system, including M$.
19
+
20
+ Jobs can be submitted to the queue directly using the api or from the
21
+ commandline using the 'bj' script. For example
22
+
23
+ code:
24
+ Bj.submit 'cat /etc/password'
25
+
26
+ cli:
27
+ bj submit cat /etc/password
28
+
29
+ When used from inside a rails application bj arranges that another process
30
+ will always be running in the background to process the jobs that you submit.
31
+ By using a separate process to run jobs bj does not impact the resource
32
+ utilization of your rails application at all and enables several very cool
33
+ features:
34
+
35
+ 1) Bj allows you to sumbit jobs to any of your configured databases and,
36
+ in each case, spawns a separate background process to run jobs from that
37
+ queue
38
+
39
+ Bj.in :production do
40
+ Bj.submit 'production_job.exe'
41
+ end
42
+
43
+ Bj.in :development do
44
+ Bj.submit 'development_job.exe'
45
+ end
46
+
47
+ 2) Although bj ensures that a process is always running to process
48
+ your jobs, you can start a proces manually. This means that any machine
49
+ capable of seeing your RAILS_ROOT can run jobs for your application, allowing
50
+ one to setup a cluster of machines doing the work of a single front end rails
51
+ applicaiton.
52
+
53
+ ________________________________
54
+ Install
55
+ --------------------------------
56
+
57
+ Bj can be installed two ways: as a gem or as a plugin.
58
+
59
+ gem:
143
60
  1) sudo gem install ThiagoLelis-backgroundjob
144
61
  2) add "require 'bj'" to config/environment.rb
62
+ 3) bj setup
63
+
64
+ plugin:
65
+ 1) ./script/plugin install http://codeforpeople.rubyforge.org/svn/rails/plugins/bj
66
+ 2) ./script/bj setup
67
+
68
+ ________________________________
69
+ Api
70
+ --------------------------------
71
+
72
+ submit jobs for background processing. 'jobs' can be a string or array of
73
+ strings. options are applied to each job in the 'jobs', and the list of
74
+ submitted jobs is always returned. options (string or symbol) can be
75
+
76
+ :rails_env => production|development|key_in_database_yml
77
+ when given this keyword causes bj to submit jobs to the
78
+ specified database. default is RAILS_ENV.
79
+
80
+ :priority => any number, including negative ones. default is zero.
81
+
82
+ :tag => a tag added to the job. simply makes searching easier.
83
+
84
+ :env => a hash specifying any additional environment vars the background
85
+ process should have.
86
+
87
+ :stdin => any stdin the background process should have.
88
+
89
+ eg:
90
+
91
+ jobs = Bj.submit 'echo foobar', :tag => 'simple job'
92
+
93
+ jobs = Bj.submit '/bin/cat', :stdin => 'in the hat', :priority => 42
94
+
95
+ jobs = Bj.submit './script/runner ./scripts/a.rb', :rails_env => 'production'
96
+
97
+ jobs = Bj.submit './script/runner /dev/stdin',
98
+ :stdin => 'p RAILS_ENV',
99
+ :tag => 'dynamic ruby code'
100
+
101
+ jobs Bj.submit array_of_commands, :priority => 451
102
+
103
+ when jobs are run, they are run in RAILS_ROOT. various attributes are
104
+ available *only* once the job has finished. you can check whether or not a
105
+ job is finished by using the #finished method, which simple does a reload and
106
+ checks to see if the exit_status is non-nil.
107
+
108
+ eg:
109
+
110
+ jobs = Bj.submit list_of_jobs, :tag => 'important'
111
+ ...
112
+
113
+ jobs.each do |job|
114
+ if job.finished?
115
+ p job.exit_status
116
+ p job.stdout
117
+ p job.stderr
118
+ end
119
+ end
120
+
121
+ See lib/bj/api.rb for more details.
122
+
123
+ ________________________________
124
+ Sponsors
125
+ --------------------------------
126
+ http://www.engineyard.com/
127
+ http://quintess.com/
128
+ http://eparklabs.com/
129
+
130
+ PARAMETERS
131
+ --rails_root=rails_root, -R (0 ~> rails_root=)
132
+ the rails_root will be guessed unless you set this
133
+ --rails_env=rails_env, -E (0 ~> rails_env=development)
134
+ set the rails_env
135
+ --log=log, -l (0 ~> log=STDERR)
136
+ set the logfile
137
+ --help, -h
138
+
139
+ AUTHOR
140
+ ara.t.howard@gmail.com
141
+
142
+ URIS
143
+ http://codeforpeople.com/lib/ruby/
144
+ http://rubyforge.org/projects/codeforpeople/
145
+ http://codeforpeople.rubyforge.org/svn/rails/plugins/
146
+
data/Rakefile CHANGED
@@ -1,22 +1,22 @@
1
- require 'rake'
2
- require 'rake/testtask'
3
- require 'rake/rdoctask'
4
-
5
- desc 'Default: run unit tests.'
6
- task :default => :test
7
-
8
- desc 'Test the bj plugin.'
9
- Rake::TestTask.new(:test) do |t|
10
- t.libs << 'lib'
11
- t.pattern = 'test/**/*_test.rb'
12
- t.verbose = true
13
- end
14
-
15
- desc 'Generate documentation for the bj plugin.'
16
- Rake::RDocTask.new(:rdoc) do |rdoc|
17
- rdoc.rdoc_dir = 'rdoc'
18
- rdoc.title = 'Bj'
19
- rdoc.options << '--line-numbers' << '--inline-source'
20
- rdoc.rdoc_files.include('README')
21
- rdoc.rdoc_files.include('lib/**/*.rb')
22
- end
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the bj plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.pattern = 'test/**/*_test.rb'
12
+ t.verbose = true
13
+ end
14
+
15
+ desc 'Generate documentation for the bj plugin.'
16
+ Rake::RDocTask.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = 'Bj'
19
+ rdoc.options << '--line-numbers' << '--inline-source'
20
+ rdoc.rdoc_files.include('README')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
data/bin/bj CHANGED
@@ -1,679 +1,679 @@
1
- #! /usr/bin/env ruby
2
-
3
- require "bj"
4
- require "main"
5
-
6
- Main {
7
- usage["description"] = <<-txt
8
- ________________________________
9
- Overview
10
- --------------------------------
11
-
12
- Backgroundjob (Bj) is a brain dead simple zero admin background priority queue
13
- for Rails. Bj is robust, platform independent (including windows), and
14
- supports internal or external manangement of the background runner process.
15
-
16
- Jobs can be submitted to the queue directly using the api or from the command
17
- line using the ./script/bj:
18
-
19
- api:
20
- Bj.submit 'cat /etc/password'
21
-
22
- command line:
23
- bj submit cat /etc/password
24
-
25
- Bj's priority queue lives in the database and is therefore durable - your jobs
26
- will live across an app crash or machine reboot. The job management is
27
- comprehensive capturing stdout, stderr, exit_status, and temporal statistics
28
- about each job:
29
-
30
- jobs = Bj.submit array_of_commands, :priority => 42
31
-
32
- ...
33
-
34
- jobs.each do |job|
35
- if job.finished?
36
- p job.stdout
37
- p job.stderr
38
- p job.exit_status
39
- p job.started_at
40
- p job.finished_at
41
- end
42
- end
43
-
44
- In addition the background runner process logs all commands run and their
45
- exit_status to a log named using the following convention:
46
-
47
- rails_root/log/bj.\#{ HOSTNAME }.\#{ RAILS_ENV }.log
48
-
49
- Bj allows you to submit jobs to multiple databases; for instance, if your
50
- application is running in development mode you may do:
51
-
52
- Bj.in :production do
53
- Bj.submit 'my_job.exe'
54
- end
55
-
56
- Bj manages the ever growing list of jobs ran by automatically archiving them
57
- into another table (by default jobs > 24 hrs old are archived) to prevent the
58
- jobs table from becoming bloated and huge.
59
-
60
- All Bj's tables are namespaced and accessible via the Bj module:
61
-
62
- Bj.table.job.find(:all) # jobs table
63
- Bj.table.job_archive.find(:all) # archived jobs
64
- Bj.table.config.find(:all) # configuration and runner state
65
-
66
- Bj always arranges for submitted jobs to run with a current working directory
67
- of RAILS_ROOT and with the correct RAILS_ENV setting. For example, if you
68
- submit a job in production it will have ENV['RAILS_ENV'] == 'production'.
69
-
70
- When Bj manages the background runner it will never outlive the rails
71
- application - it is started and stopped on demand as the rails app is started
72
- and stopped. This is also true for ./script/console - Bj will automatically
73
- fire off the background runner to process jobs submitted using the console.
74
-
75
- Bj ensures that only one background process is running for your application -
76
- firing up three mongrels or fcgi processes will result in only one background
77
- runner being started. Note that the number of background runners does not
78
- determine throughput - that is determined primarily by the nature of the jobs
79
- themselves and how much work they perform per process.
80
-
81
-
82
- ________________________________
83
- Architecture
84
- --------------------------------
85
-
86
- If one ignores platform specific details the design of Bj is quite simple: the
87
- main Rails application submits jobs to table, stored in the database. The act
88
- of submitting triggers exactly one of two things to occur:
89
-
90
- 1) a new long running background runner to be started
91
-
92
- 2) an existing background runner to be signaled
93
-
94
- The background runner refuses to run two copies of itself for a given
95
- hostname/rails_env combination. For example you may only have one background
96
- runner processing jobs on localhost in development mode.
97
-
98
- The background runner, under normal circumstances, is managed by Bj itself -
99
- you need do nothing to start, monitor, or stop it - it just works. However,
100
- some people will prefer manage their own background process, see 'External
101
- Runner' section below for more on this.
102
-
103
- The runner simply processes each job in a highest priority oldest-in fashion,
104
- capturing stdout, stderr, exit_status, etc. and storing the information back
105
- into the database while logging it's actions. When there are no jobs to run
106
- the runner goes to sleep for 42 seconds; however this sleep is interuptable,
107
- such as when the runner is signaled that a new job has been submitted so,
108
- under normal circumstances there will be zero lag between job submission and
109
- job running for an empty queue.
110
-
111
-
112
- ________________________________
113
- External Runner / Clustering
114
- --------------------------------
115
-
116
- For the paranoid control freaks out there (myself included) it is quite
117
- possible to manage and monitor the runner process manually. This can be
118
- desirable in production setups where monitoring software may kill leaking
119
- rails apps periodically.
120
-
121
- Recalling that Bj will only allow one copy of itself to process jobs per
122
- hostname/rails_env pair we can simply do something like this in cron
123
-
124
- cmd = bj run --forever \\
125
- --rails_env=development \\
126
- --rails_root=/Users/ahoward/rails_root
127
-
128
- */15 * * * * $cmd
129
-
130
- this will simply attempt the start the background runner every 15 minutes if,
131
- and only if, it's not *already* running.
132
-
133
- In addtion to this you'll want to tell Bj not to manage the runner itself
134
- using
135
-
136
- Bj.config["production.no_tickle"] = true
137
-
138
- Note that, for clusting setups, it's as simple as adding a crontab and config
139
- entry like this for each host. Because Bj throttles background runners per
140
- hostname this will allow one runner per hostname - making it quite simple to
141
- cluster three nodes behind a besieged rails application.
142
-
143
-
144
- ________________________________
145
- Designing Jobs
146
- --------------------------------
147
-
148
- Bj runs it's jobs as command line applications. It ensures that all jobs run
149
- in RAILS_ROOT so it's quite natural to apply a pattern such as
150
-
151
- mkdir ./jobs
152
- edit ./jobs/background_job_to_run
153
-
154
- ...
155
-
156
- Bj.submit "./jobs/background_job_to_run"
157
-
158
- If you need to run you jobs under an entire rails environment you'll need to
159
- do this:
160
-
161
- Bj.submit "./script/runner ./jobs/background_job_to_run"
162
-
163
- Obviously "./script/runner" loads the rails environment for you. It's worth
164
- noting that this happens for each job and that this is by design: the reason
165
- is that most rails applications leak memory like a sieve so, if one were to
166
- spawn a long running process that used the application code base you'd have a
167
- lovely doubling of memory usage on you app servers. Although loading the
168
- rails environment for each background job requires a little time, a little
169
- cpu, and a lot less memory. A future version of Bj will provide a way to load
170
- the rails environment once and to process background jobs in this environment,
171
- but anyone wanting to use this in production will be required to duct tape
172
- their entire chest and have a team of oxen rip off the tape without screaming
173
- to prove steelyness of spirit and profound understanding of the other side.
174
-
175
- Don't forget that you can submit jobs with command line arguments:
176
-
177
- Bj.submit "./jobs/a.rb 1 foobar --force"
178
-
179
- and that you can do powerful things by passing stdin to a job that powers
180
- through a list of work. For instance, assume a "./jobs/bulkmail" job
181
- resembling
182
-
183
- STDIN.each do |line|
184
- address = line.strip
185
- mail_message_to address
186
- end
187
-
188
- then you could
189
-
190
- stdin = [
191
- "foo@bar.com",
192
- "bar@foo.com",
193
- "ara.t.howard@codeforpeople.com",
194
- ]
195
-
196
- Bj.submit "./script/runner ./jobs/bulkmail", :stdin => stdin
197
-
198
- and all those emails would be sent in the background.
199
-
200
- Bj's power is putting jobs in the background in a simple and robust fashion.
201
- It's your task to build intelligent jobs that leverage batch processing, and
202
- other, possibilities. The upshot of building tasks this way is that they are
203
- quite easy to test before submitting them from inside your application.
204
-
205
-
206
- ________________________________
207
- Install
208
- --------------------------------
209
-
210
- Bj can be installed two ways: as a plugin or via rubygems
211
-
212
- plugin:
213
- 1) ./script/plugin install http://codeforpeople.rubyforge.org/svn/rails/plugins/bj
214
- 2) ./script/bj setup
215
-
216
- gem:
217
- 1) $sudo gem install bj
218
- 2) add "require 'bj'" to config/environment.rb
219
- 3) bj setup
220
-
221
- ________________________________
222
- Api
223
- --------------------------------
224
-
225
- submit jobs for background processing. 'jobs' can be a string or array of
226
- strings. options are applied to each job in the 'jobs', and the list of
227
- submitted jobs is always returned. options (string or symbol) can be
228
-
229
- :rails_env => production|development|key_in_database_yml
230
- when given this keyword causes bj to submit jobs to the
231
- specified database. default is RAILS_ENV.
232
-
233
- :priority => any number, including negative ones. default is zero.
234
-
235
- :tag => a tag added to the job. simply makes searching easier.
236
-
237
- :env => a hash specifying any additional environment vars the background
238
- process should have.
239
-
240
- :stdin => any stdin the background process should have. must respond_to
241
- to_s
242
-
243
- eg:
244
-
245
- jobs = Bj.submit 'echo foobar', :tag => 'simple job'
246
-
247
- jobs = Bj.submit '/bin/cat', :stdin => 'in the hat', :priority => 42
248
-
249
- jobs = Bj.submit './script/runner ./scripts/a.rb', :rails_env => 'production'
250
-
251
- jobs = Bj.submit './script/runner /dev/stdin',
252
- :stdin => 'p RAILS_ENV',
253
- :tag => 'dynamic ruby code'
254
-
255
- jobs Bj.submit array_of_commands, :priority => 451
256
-
257
- when jobs are run, they are run in RAILS_ROOT. various attributes are
258
- available *only* once the job has finished. you can check whether or not a
259
- job is finished by using the #finished method, which simple does a reload and
260
- checks to see if the exit_status is non-nil.
261
-
262
- eg:
263
-
264
- jobs = Bj.submit list_of_jobs, :tag => 'important'
265
- ...
266
-
267
- jobs.each do |job|
268
- if job.finished?
269
- p job.exit_status
270
- p job.stdout
271
- p job.stderr
272
- end
273
- end
274
-
275
- See lib/bj/api.rb for more details.
276
-
277
- ________________________________
278
- Sponsors
279
- --------------------------------
280
- http://quintess.com/
281
- http://www.engineyard.com/
282
- http://igicom.com/
283
- http://eparklabs.com/
284
-
285
- http://your_company.com/ <<-- (targeted marketing aimed at *you*)
286
-
287
- ________________________________
288
- Version
289
- --------------------------------
290
- #{ Bj.version }
291
- txt
292
-
293
- usage["uris"] = <<-txt
294
- http://codeforpeople.com/lib/ruby/
295
- http://rubyforge.org/projects/codeforpeople/
296
- http://codeforpeople.rubyforge.org/svn/rails/plugins/
297
- txt
298
-
299
- author "ara.t.howard@gmail.com"
300
-
301
- option("rails_root", "R"){
302
- description "the rails_root will be guessed unless you set this"
303
- argument_required
304
- default RAILS_ROOT
305
- }
306
-
307
- option("rails_env", "E"){
308
- description "set the rails_env"
309
- argument_required
310
- default RAILS_ENV
311
- }
312
-
313
- option("log", "l"){
314
- description "set the logfile"
315
- argument_required
316
- default STDERR
317
- }
318
-
319
-
320
- mode "migration_code" do
321
- description "dump migration code on stdout"
322
-
323
- def run
324
- puts Bj.table.migration_code
325
- end
326
- end
327
-
328
- mode "generate_migration" do
329
- description "generate a migration"
330
-
331
- def run
332
- Bj.generate_migration
333
- end
334
- end
335
-
336
- mode "migrate" do
337
- description "migrate the db"
338
-
339
- def run
340
- Bj.migrate
341
- end
342
- end
343
-
344
- mode "setup" do
345
- description "generate a migration and migrate"
346
-
347
- def run
348
- set_rails_env(argv.first) if argv.first
349
- Bj.setup
350
- end
351
- end
352
-
353
- mode "plugin" do
354
- description "dump the plugin into rails_root"
355
-
356
- def run
357
- Bj.plugin
358
- end
359
- end
360
-
361
- mode "run" do
362
- description "start a job runnner, possibly as a daemon"
363
-
364
- option("--forever"){}
365
- option("--ppid"){
366
- argument :required
367
- cast :integer
368
- }
369
- option("--wait"){
370
- argument :required
371
- cast :integer
372
- }
373
- option("--limit"){
374
- argument :required
375
- cast :integer
376
- }
377
- option("--redirect"){
378
- argument :required
379
- }
380
- option("--daemon"){}
381
-
382
- def run
383
- options = {}
384
-
385
- =begin
386
- %w[ forever ].each do |key|
387
- options[key.to_sym] = true if param[key].given?
388
- end
389
- =end
390
-
391
- %w[ forever ppid wait limit ].each do |key|
392
- options[key.to_sym] = param[key].value if param[key].given?
393
- end
394
-
395
- #p options
396
- #exit
397
- if param["redirect"].given?
398
- open(param["redirect"].value, "a+") do |fd|
399
- STDERR.reopen fd
400
- STDOUT.reopen fd
401
- end
402
- STDERR.sync = true
403
- STDOUT.sync = true
404
- end
405
-
406
- trap("SIGTERM"){
407
- info{ "SIGTERM" }
408
- exit
409
- }
410
-
411
- if param["daemon"].given?
412
- daemon{ Bj.run options }
413
- else
414
- Bj.run options
415
- end
416
- end
417
- end
418
-
419
- mode "submit" do
420
- keyword("file"){
421
- argument :required
422
- attr
423
- }
424
-
425
- def run
426
- joblist = Bj.joblist.for argv.join(' ')
427
-
428
- case file
429
- when "-"
430
- joblist.push(Bj.joblist.jobs_from_io(STDIN))
431
- when "--", "---"
432
- joblist.push(Bj.joblist.jobs_from_yaml(STDIN))
433
- else
434
- open(file){|io| joblist.push(Bj.joblist.jobs_from_io(io)) }
435
- end
436
-
437
- jobs = Bj.submit joblist, :no_tickle => true
438
-
439
- oh = lambda{|job| OrderedHash["id", job.id, "command", job.command]}
440
-
441
- y jobs.map{|job| oh[job]}
442
- end
443
- end
444
-
445
- mode "list" do
446
- def run
447
- Bj.transaction do
448
- y Bj::Table::Job.find(:all).map(&:to_hash)
449
- end
450
- end
451
- end
452
-
453
- mode "set" do
454
- argument("key"){ attr }
455
-
456
- argument("value"){ attr }
457
-
458
- option("hostname", "H"){
459
- argument :required
460
- default Bj.hostname
461
- attr
462
- }
463
-
464
- option("cast", "c"){
465
- argument :required
466
- default "to_s"
467
- attr
468
- }
469
-
470
- def run
471
- Bj.transaction do
472
- Bj.config.set(key, value, :hostname => hostname, :cast => cast)
473
- y Bj.table.config.for(:hostname => hostname)
474
- end
475
- end
476
- end
477
-
478
- mode "config" do
479
- option("hostname", "H"){
480
- argument :required
481
- default Bj.hostname
482
- }
483
-
484
- def run
485
- Bj.transaction do
486
- y Bj.table.config.for(:hostname => param["hostname"].value)
487
- end
488
- end
489
- end
490
-
491
- mode "pid" do
492
- option("hostname", "H"){
493
- argument :required
494
- default Bj.hostname
495
- }
496
-
497
- def run
498
- Bj.transaction do
499
- config = Bj.table.config.for(:hostname => param["hostname"].value)
500
- puts config[ "#{ RAILS_ENV }.pid" ] if config
501
- end
502
- end
503
- end
504
-
505
-
506
- def run
507
- help!
508
- end
509
-
510
- def before_run
511
- self.logger = param["log"].value
512
- Bj.logger = logger
513
- set_rails_root(param["rails_root"].value) if param["rails_root"].given?
514
- set_rails_env(param["rails_env"].value) if param["rails_env"].given?
515
- end
516
-
517
- def set_rails_root rails_root
518
- ENV["RAILS_ROOT"] = rails_root
519
- ::Object.instance_eval do
520
- remove_const :RAILS_ROOT
521
- const_set :RAILS_ROOT, rails_root
522
- end
523
- end
524
-
525
- def set_rails_env rails_env
526
- ENV["RAILS_ENV"] = rails_env
527
- ::Object.instance_eval do
528
- remove_const :RAILS_ENV
529
- const_set :RAILS_ENV, rails_env
530
- end
531
- end
532
-
533
- def daemon
534
- ra, wa = IO.pipe
535
- rb, wb = IO.pipe
536
- if fork
537
- at_exit{ exit! }
538
- wa.close
539
- r = ra
540
- rb.close
541
- w = wb
542
- pid = r.gets
543
- w.puts pid
544
- Integer pid.strip
545
- else
546
- ra.close
547
- w = wa
548
- wb.close
549
- r = rb
550
- open("/dev/null", "r+") do |fd|
551
- STDIN.reopen fd
552
- STDOUT.reopen fd
553
- STDERR.reopen fd
554
- end
555
- Process::setsid rescue nil
556
- pid =
557
- fork do
558
- Dir::chdir RAILS_ROOT
559
- File::umask 0
560
- $DAEMON = true
561
- yield
562
- exit!
563
- end
564
- w.puts pid
565
- r.gets
566
- exit!
567
- end
568
- end
569
- }
570
-
571
-
572
-
573
-
574
-
575
- #
576
- # we setup a few things so the script works regardless of whether it was
577
- # called out of /usr/local/bin, ./script, or wherever. note that the script
578
- # does *not* require the entire rails application to be loaded into memory!
579
- # we could just load boot.rb and environment.rb, but this method let's
580
- # submitting and running jobs be infinitely more lightweight.
581
- #
582
-
583
- BEGIN {
584
- #
585
- # see if we're running out of RAILS_ROOT/script/
586
- #
587
- unless defined?(BJ_SCRIPT)
588
- BJ_SCRIPT =
589
- if %w[ script config app ].map{|d| test ?d, "#{ File.dirname __FILE__ }/../#{ d }"}.all?
590
- __FILE__
591
- else
592
- nil
593
- end
594
- end
595
- #
596
- # setup RAILS_ROOT
597
- #
598
- unless defined?(RAILS_ROOT)
599
- ### grab env var first
600
- rails_root = ENV["RAILS_ROOT"]
601
-
602
- ### commandline usage clobbers
603
- kv = nil
604
- ARGV.delete_if{|arg| arg =~ %r/^RAILS_ROOT=/ and kv = arg}
605
- rails_root = kv.split(%r/=/,2).last if kv
606
-
607
- ### we know the rails_root if we are in RAILS_ROOT/script/
608
- unless rails_root
609
- if BJ_SCRIPT
610
- rails_root = File.expand_path "#{ File.dirname __FILE__ }/.."
611
- end
612
- end
613
-
614
- ### perhaps the current directory is a rails_root?
615
- unless rails_root
616
- if %w[ script config app ].map{|d| test(?d, d)}.all?
617
- rails_root = File.expand_path "."
618
- end
619
- end
620
-
621
- ### bootstrap
622
- RAILS_ROOT = rails_root
623
- end
624
- #
625
- # setup RAILS_ENV
626
- #
627
- unless defined?(RAILS_ENV)
628
- ### grab env var first
629
- rails_env = ENV["RAILS_ENV"]
630
-
631
- ### commandline usage clobbers
632
- kv = nil
633
- ARGV.delete_if{|arg| arg =~ %r/^RAILS_ENV=/ and kv = arg}
634
- rails_env = kv.split(%r/=/,2).last if kv
635
-
636
- ### fallback to development
637
- unless rails_env
638
- rails_env = "development"
639
- end
640
-
641
- ### bootstrap
642
- RAILS_ENV = rails_env
643
- end
644
- #
645
- # ensure that rubygems is loaded
646
- #
647
- begin
648
- require "rubygems"
649
- rescue
650
- 42
651
- end
652
- #
653
- # load gems from plugin dir iff installed as plugin - otherwise load normally
654
- #
655
- if RAILS_ROOT and BJ_SCRIPT
656
- =begin
657
- dir = Gem.dir
658
- path = Gem.path
659
- gem_home = File.join RAILS_ROOT, "vendor", "plugins", "bj", "gem_home"
660
- gem_path = [gem_home]
661
- Gem.send :use_paths, gem_home, gem_path
662
- gem "bj"
663
- require "bj"
664
- gem "main"
665
- require "main"
666
- =end
667
- libdir = File.join(RAILS_ROOT, "vendor", "plugins", "bj", "lib")
668
- $LOAD_PATH.unshift libdir
669
- end
670
- #
671
- # hack of #to_s of STDERR/STDOUT for nice help messages
672
- #
673
- class << STDERR
674
- def to_s() 'STDERR' end
675
- end
676
- class << STDOUT
677
- def to_s() 'STDOUT' end
678
- end
679
- }
1
+ #! /usr/bin/env ruby
2
+
3
+ require "bj"
4
+ require "main"
5
+
6
+ Main {
7
+ usage["description"] = <<-txt
8
+ ________________________________
9
+ Overview
10
+ --------------------------------
11
+
12
+ Backgroundjob (Bj) is a brain dead simple zero admin background priority queue
13
+ for Rails. Bj is robust, platform independent (including windows), and
14
+ supports internal or external manangement of the background runner process.
15
+
16
+ Jobs can be submitted to the queue directly using the api or from the command
17
+ line using the ./script/bj:
18
+
19
+ api:
20
+ Bj.submit 'cat /etc/password'
21
+
22
+ command line:
23
+ bj submit cat /etc/password
24
+
25
+ Bj's priority queue lives in the database and is therefore durable - your jobs
26
+ will live across an app crash or machine reboot. The job management is
27
+ comprehensive capturing stdout, stderr, exit_status, and temporal statistics
28
+ about each job:
29
+
30
+ jobs = Bj.submit array_of_commands, :priority => 42
31
+
32
+ ...
33
+
34
+ jobs.each do |job|
35
+ if job.finished?
36
+ p job.stdout
37
+ p job.stderr
38
+ p job.exit_status
39
+ p job.started_at
40
+ p job.finished_at
41
+ end
42
+ end
43
+
44
+ In addition the background runner process logs all commands run and their
45
+ exit_status to a log named using the following convention:
46
+
47
+ rails_root/log/bj.\#{ HOSTNAME }.\#{ RAILS_ENV }.log
48
+
49
+ Bj allows you to submit jobs to multiple databases; for instance, if your
50
+ application is running in development mode you may do:
51
+
52
+ Bj.in :production do
53
+ Bj.submit 'my_job.exe'
54
+ end
55
+
56
+ Bj manages the ever growing list of jobs ran by automatically archiving them
57
+ into another table (by default jobs > 24 hrs old are archived) to prevent the
58
+ jobs table from becoming bloated and huge.
59
+
60
+ All Bj's tables are namespaced and accessible via the Bj module:
61
+
62
+ Bj.table.job.find(:all) # jobs table
63
+ Bj.table.job_archive.find(:all) # archived jobs
64
+ Bj.table.config.find(:all) # configuration and runner state
65
+
66
+ Bj always arranges for submitted jobs to run with a current working directory
67
+ of RAILS_ROOT and with the correct RAILS_ENV setting. For example, if you
68
+ submit a job in production it will have ENV['RAILS_ENV'] == 'production'.
69
+
70
+ When Bj manages the background runner it will never outlive the rails
71
+ application - it is started and stopped on demand as the rails app is started
72
+ and stopped. This is also true for ./script/console - Bj will automatically
73
+ fire off the background runner to process jobs submitted using the console.
74
+
75
+ Bj ensures that only one background process is running for your application -
76
+ firing up three mongrels or fcgi processes will result in only one background
77
+ runner being started. Note that the number of background runners does not
78
+ determine throughput - that is determined primarily by the nature of the jobs
79
+ themselves and how much work they perform per process.
80
+
81
+
82
+ ________________________________
83
+ Architecture
84
+ --------------------------------
85
+
86
+ If one ignores platform specific details the design of Bj is quite simple: the
87
+ main Rails application submits jobs to table, stored in the database. The act
88
+ of submitting triggers exactly one of two things to occur:
89
+
90
+ 1) a new long running background runner to be started
91
+
92
+ 2) an existing background runner to be signaled
93
+
94
+ The background runner refuses to run two copies of itself for a given
95
+ hostname/rails_env combination. For example you may only have one background
96
+ runner processing jobs on localhost in development mode.
97
+
98
+ The background runner, under normal circumstances, is managed by Bj itself -
99
+ you need do nothing to start, monitor, or stop it - it just works. However,
100
+ some people will prefer manage their own background process, see 'External
101
+ Runner' section below for more on this.
102
+
103
+ The runner simply processes each job in a highest priority oldest-in fashion,
104
+ capturing stdout, stderr, exit_status, etc. and storing the information back
105
+ into the database while logging it's actions. When there are no jobs to run
106
+ the runner goes to sleep for 42 seconds; however this sleep is interuptable,
107
+ such as when the runner is signaled that a new job has been submitted so,
108
+ under normal circumstances there will be zero lag between job submission and
109
+ job running for an empty queue.
110
+
111
+
112
+ ________________________________
113
+ External Runner / Clustering
114
+ --------------------------------
115
+
116
+ For the paranoid control freaks out there (myself included) it is quite
117
+ possible to manage and monitor the runner process manually. This can be
118
+ desirable in production setups where monitoring software may kill leaking
119
+ rails apps periodically.
120
+
121
+ Recalling that Bj will only allow one copy of itself to process jobs per
122
+ hostname/rails_env pair we can simply do something like this in cron
123
+
124
+ cmd = bj run --forever \\
125
+ --rails_env=development \\
126
+ --rails_root=/Users/ahoward/rails_root
127
+
128
+ */15 * * * * $cmd
129
+
130
+ this will simply attempt the start the background runner every 15 minutes if,
131
+ and only if, it's not *already* running.
132
+
133
+ In addtion to this you'll want to tell Bj not to manage the runner itself
134
+ using
135
+
136
+ Bj.config["production.no_tickle"] = true
137
+
138
+ Note that, for clusting setups, it's as simple as adding a crontab and config
139
+ entry like this for each host. Because Bj throttles background runners per
140
+ hostname this will allow one runner per hostname - making it quite simple to
141
+ cluster three nodes behind a besieged rails application.
142
+
143
+
144
+ ________________________________
145
+ Designing Jobs
146
+ --------------------------------
147
+
148
+ Bj runs it's jobs as command line applications. It ensures that all jobs run
149
+ in RAILS_ROOT so it's quite natural to apply a pattern such as
150
+
151
+ mkdir ./jobs
152
+ edit ./jobs/background_job_to_run
153
+
154
+ ...
155
+
156
+ Bj.submit "./jobs/background_job_to_run"
157
+
158
+ If you need to run you jobs under an entire rails environment you'll need to
159
+ do this:
160
+
161
+ Bj.submit "./script/runner ./jobs/background_job_to_run"
162
+
163
+ Obviously "./script/runner" loads the rails environment for you. It's worth
164
+ noting that this happens for each job and that this is by design: the reason
165
+ is that most rails applications leak memory like a sieve so, if one were to
166
+ spawn a long running process that used the application code base you'd have a
167
+ lovely doubling of memory usage on you app servers. Although loading the
168
+ rails environment for each background job requires a little time, a little
169
+ cpu, and a lot less memory. A future version of Bj will provide a way to load
170
+ the rails environment once and to process background jobs in this environment,
171
+ but anyone wanting to use this in production will be required to duct tape
172
+ their entire chest and have a team of oxen rip off the tape without screaming
173
+ to prove steelyness of spirit and profound understanding of the other side.
174
+
175
+ Don't forget that you can submit jobs with command line arguments:
176
+
177
+ Bj.submit "./jobs/a.rb 1 foobar --force"
178
+
179
+ and that you can do powerful things by passing stdin to a job that powers
180
+ through a list of work. For instance, assume a "./jobs/bulkmail" job
181
+ resembling
182
+
183
+ STDIN.each do |line|
184
+ address = line.strip
185
+ mail_message_to address
186
+ end
187
+
188
+ then you could
189
+
190
+ stdin = [
191
+ "foo@bar.com",
192
+ "bar@foo.com",
193
+ "ara.t.howard@codeforpeople.com",
194
+ ]
195
+
196
+ Bj.submit "./script/runner ./jobs/bulkmail", :stdin => stdin
197
+
198
+ and all those emails would be sent in the background.
199
+
200
+ Bj's power is putting jobs in the background in a simple and robust fashion.
201
+ It's your task to build intelligent jobs that leverage batch processing, and
202
+ other, possibilities. The upshot of building tasks this way is that they are
203
+ quite easy to test before submitting them from inside your application.
204
+
205
+
206
+ ________________________________
207
+ Install
208
+ --------------------------------
209
+
210
+ Bj can be installed two ways: as a plugin or via rubygems
211
+
212
+ plugin:
213
+ 1) ./script/plugin install http://codeforpeople.rubyforge.org/svn/rails/plugins/bj
214
+ 2) ./script/bj setup
215
+
216
+ gem:
217
+ 1) $sudo gem install bj
218
+ 2) add "require 'bj'" to config/environment.rb
219
+ 3) bj setup
220
+
221
+ ________________________________
222
+ Api
223
+ --------------------------------
224
+
225
+ submit jobs for background processing. 'jobs' can be a string or array of
226
+ strings. options are applied to each job in the 'jobs', and the list of
227
+ submitted jobs is always returned. options (string or symbol) can be
228
+
229
+ :rails_env => production|development|key_in_database_yml
230
+ when given this keyword causes bj to submit jobs to the
231
+ specified database. default is RAILS_ENV.
232
+
233
+ :priority => any number, including negative ones. default is zero.
234
+
235
+ :tag => a tag added to the job. simply makes searching easier.
236
+
237
+ :env => a hash specifying any additional environment vars the background
238
+ process should have.
239
+
240
+ :stdin => any stdin the background process should have. must respond_to
241
+ to_s
242
+
243
+ eg:
244
+
245
+ jobs = Bj.submit 'echo foobar', :tag => 'simple job'
246
+
247
+ jobs = Bj.submit '/bin/cat', :stdin => 'in the hat', :priority => 42
248
+
249
+ jobs = Bj.submit './script/runner ./scripts/a.rb', :rails_env => 'production'
250
+
251
+ jobs = Bj.submit './script/runner /dev/stdin',
252
+ :stdin => 'p RAILS_ENV',
253
+ :tag => 'dynamic ruby code'
254
+
255
+ jobs Bj.submit array_of_commands, :priority => 451
256
+
257
+ when jobs are run, they are run in RAILS_ROOT. various attributes are
258
+ available *only* once the job has finished. you can check whether or not a
259
+ job is finished by using the #finished method, which simple does a reload and
260
+ checks to see if the exit_status is non-nil.
261
+
262
+ eg:
263
+
264
+ jobs = Bj.submit list_of_jobs, :tag => 'important'
265
+ ...
266
+
267
+ jobs.each do |job|
268
+ if job.finished?
269
+ p job.exit_status
270
+ p job.stdout
271
+ p job.stderr
272
+ end
273
+ end
274
+
275
+ See lib/bj/api.rb for more details.
276
+
277
+ ________________________________
278
+ Sponsors
279
+ --------------------------------
280
+ http://quintess.com/
281
+ http://www.engineyard.com/
282
+ http://igicom.com/
283
+ http://eparklabs.com/
284
+
285
+ http://your_company.com/ <<-- (targeted marketing aimed at *you*)
286
+
287
+ ________________________________
288
+ Version
289
+ --------------------------------
290
+ #{ Bj.version }
291
+ txt
292
+
293
+ usage["uris"] = <<-txt
294
+ http://codeforpeople.com/lib/ruby/
295
+ http://rubyforge.org/projects/codeforpeople/
296
+ http://codeforpeople.rubyforge.org/svn/rails/plugins/
297
+ txt
298
+
299
+ author "ara.t.howard@gmail.com"
300
+
301
+ option("rails_root", "R"){
302
+ description "the rails_root will be guessed unless you set this"
303
+ argument_required
304
+ default RAILS_ROOT
305
+ }
306
+
307
+ option("rails_env", "E"){
308
+ description "set the rails_env"
309
+ argument_required
310
+ default RAILS_ENV
311
+ }
312
+
313
+ option("log", "l"){
314
+ description "set the logfile"
315
+ argument_required
316
+ default STDERR
317
+ }
318
+
319
+
320
+ mode "migration_code" do
321
+ description "dump migration code on stdout"
322
+
323
+ def run
324
+ puts Bj.table.migration_code
325
+ end
326
+ end
327
+
328
+ mode "generate_migration" do
329
+ description "generate a migration"
330
+
331
+ def run
332
+ Bj.generate_migration
333
+ end
334
+ end
335
+
336
+ mode "migrate" do
337
+ description "migrate the db"
338
+
339
+ def run
340
+ Bj.migrate
341
+ end
342
+ end
343
+
344
+ mode "setup" do
345
+ description "generate a migration and migrate"
346
+
347
+ def run
348
+ set_rails_env(argv.first) if argv.first
349
+ Bj.setup
350
+ end
351
+ end
352
+
353
+ mode "plugin" do
354
+ description "dump the plugin into rails_root"
355
+
356
+ def run
357
+ Bj.plugin
358
+ end
359
+ end
360
+
361
+ mode "run" do
362
+ description "start a job runnner, possibly as a daemon"
363
+
364
+ option("--forever"){}
365
+ option("--ppid"){
366
+ argument :required
367
+ cast :integer
368
+ }
369
+ option("--wait"){
370
+ argument :required
371
+ cast :integer
372
+ }
373
+ option("--limit"){
374
+ argument :required
375
+ cast :integer
376
+ }
377
+ option("--redirect"){
378
+ argument :required
379
+ }
380
+ option("--daemon"){}
381
+
382
+ def run
383
+ options = {}
384
+
385
+ =begin
386
+ %w[ forever ].each do |key|
387
+ options[key.to_sym] = true if param[key].given?
388
+ end
389
+ =end
390
+
391
+ %w[ forever ppid wait limit ].each do |key|
392
+ options[key.to_sym] = param[key].value if param[key].given?
393
+ end
394
+
395
+ #p options
396
+ #exit
397
+ if param["redirect"].given?
398
+ open(param["redirect"].value, "a+") do |fd|
399
+ STDERR.reopen fd
400
+ STDOUT.reopen fd
401
+ end
402
+ STDERR.sync = true
403
+ STDOUT.sync = true
404
+ end
405
+
406
+ trap("SIGTERM"){
407
+ info{ "SIGTERM" }
408
+ exit
409
+ }
410
+
411
+ if param["daemon"].given?
412
+ daemon{ Bj.run options }
413
+ else
414
+ Bj.run options
415
+ end
416
+ end
417
+ end
418
+
419
+ mode "submit" do
420
+ keyword("file"){
421
+ argument :required
422
+ attr
423
+ }
424
+
425
+ def run
426
+ joblist = Bj.joblist.for argv.join(' ')
427
+
428
+ case file
429
+ when "-"
430
+ joblist.push(Bj.joblist.jobs_from_io(STDIN))
431
+ when "--", "---"
432
+ joblist.push(Bj.joblist.jobs_from_yaml(STDIN))
433
+ else
434
+ open(file){|io| joblist.push(Bj.joblist.jobs_from_io(io)) }
435
+ end
436
+
437
+ jobs = Bj.submit joblist, :no_tickle => true
438
+
439
+ oh = lambda{|job| OrderedHash["id", job.id, "command", job.command]}
440
+
441
+ y jobs.map{|job| oh[job]}
442
+ end
443
+ end
444
+
445
+ mode "list" do
446
+ def run
447
+ Bj.transaction do
448
+ y Bj::Table::Job.find(:all).map(&:to_hash)
449
+ end
450
+ end
451
+ end
452
+
453
+ mode "set" do
454
+ argument("key"){ attr }
455
+
456
+ argument("value"){ attr }
457
+
458
+ option("hostname", "H"){
459
+ argument :required
460
+ default Bj.hostname
461
+ attr
462
+ }
463
+
464
+ option("cast", "c"){
465
+ argument :required
466
+ default "to_s"
467
+ attr
468
+ }
469
+
470
+ def run
471
+ Bj.transaction do
472
+ Bj.config.set(key, value, :hostname => hostname, :cast => cast)
473
+ y Bj.table.config.for(:hostname => hostname)
474
+ end
475
+ end
476
+ end
477
+
478
+ mode "config" do
479
+ option("hostname", "H"){
480
+ argument :required
481
+ default Bj.hostname
482
+ }
483
+
484
+ def run
485
+ Bj.transaction do
486
+ y Bj.table.config.for(:hostname => param["hostname"].value)
487
+ end
488
+ end
489
+ end
490
+
491
+ mode "pid" do
492
+ option("hostname", "H"){
493
+ argument :required
494
+ default Bj.hostname
495
+ }
496
+
497
+ def run
498
+ Bj.transaction do
499
+ config = Bj.table.config.for(:hostname => param["hostname"].value)
500
+ puts config[ "#{ RAILS_ENV }.pid" ] if config
501
+ end
502
+ end
503
+ end
504
+
505
+
506
+ def run
507
+ help!
508
+ end
509
+
510
+ def before_run
511
+ self.logger = param["log"].value
512
+ Bj.logger = logger
513
+ set_rails_root(param["rails_root"].value) if param["rails_root"].given?
514
+ set_rails_env(param["rails_env"].value) if param["rails_env"].given?
515
+ end
516
+
517
+ def set_rails_root rails_root
518
+ ENV["RAILS_ROOT"] = rails_root
519
+ ::Object.instance_eval do
520
+ remove_const :RAILS_ROOT
521
+ const_set :RAILS_ROOT, rails_root
522
+ end
523
+ end
524
+
525
+ def set_rails_env rails_env
526
+ ENV["RAILS_ENV"] = rails_env
527
+ ::Object.instance_eval do
528
+ remove_const :RAILS_ENV
529
+ const_set :RAILS_ENV, rails_env
530
+ end
531
+ end
532
+
533
+ def daemon
534
+ ra, wa = IO.pipe
535
+ rb, wb = IO.pipe
536
+ if fork
537
+ at_exit{ exit! }
538
+ wa.close
539
+ r = ra
540
+ rb.close
541
+ w = wb
542
+ pid = r.gets
543
+ w.puts pid
544
+ Integer pid.strip
545
+ else
546
+ ra.close
547
+ w = wa
548
+ wb.close
549
+ r = rb
550
+ open("/dev/null", "r+") do |fd|
551
+ STDIN.reopen fd
552
+ STDOUT.reopen fd
553
+ STDERR.reopen fd
554
+ end
555
+ Process::setsid rescue nil
556
+ pid =
557
+ fork do
558
+ Dir::chdir RAILS_ROOT
559
+ File::umask 0
560
+ $DAEMON = true
561
+ yield
562
+ exit!
563
+ end
564
+ w.puts pid
565
+ r.gets
566
+ exit!
567
+ end
568
+ end
569
+ }
570
+
571
+
572
+
573
+
574
+
575
+ #
576
+ # we setup a few things so the script works regardless of whether it was
577
+ # called out of /usr/local/bin, ./script, or wherever. note that the script
578
+ # does *not* require the entire rails application to be loaded into memory!
579
+ # we could just load boot.rb and environment.rb, but this method let's
580
+ # submitting and running jobs be infinitely more lightweight.
581
+ #
582
+
583
+ BEGIN {
584
+ #
585
+ # see if we're running out of RAILS_ROOT/script/
586
+ #
587
+ unless defined?(BJ_SCRIPT)
588
+ BJ_SCRIPT =
589
+ if %w[ script config app ].map{|d| test ?d, "#{ File.dirname __FILE__ }/../#{ d }"}.all?
590
+ __FILE__
591
+ else
592
+ nil
593
+ end
594
+ end
595
+ #
596
+ # setup RAILS_ROOT
597
+ #
598
+ unless defined?(RAILS_ROOT)
599
+ ### grab env var first
600
+ rails_root = ENV["RAILS_ROOT"]
601
+
602
+ ### commandline usage clobbers
603
+ kv = nil
604
+ ARGV.delete_if{|arg| arg =~ %r/^RAILS_ROOT=/ and kv = arg}
605
+ rails_root = kv.split(%r/=/,2).last if kv
606
+
607
+ ### we know the rails_root if we are in RAILS_ROOT/script/
608
+ unless rails_root
609
+ if BJ_SCRIPT
610
+ rails_root = File.expand_path "#{ File.dirname __FILE__ }/.."
611
+ end
612
+ end
613
+
614
+ ### perhaps the current directory is a rails_root?
615
+ unless rails_root
616
+ if %w[ script config app ].map{|d| test(?d, d)}.all?
617
+ rails_root = File.expand_path "."
618
+ end
619
+ end
620
+
621
+ ### bootstrap
622
+ RAILS_ROOT = rails_root
623
+ end
624
+ #
625
+ # setup RAILS_ENV
626
+ #
627
+ unless defined?(RAILS_ENV)
628
+ ### grab env var first
629
+ rails_env = ENV["RAILS_ENV"]
630
+
631
+ ### commandline usage clobbers
632
+ kv = nil
633
+ ARGV.delete_if{|arg| arg =~ %r/^RAILS_ENV=/ and kv = arg}
634
+ rails_env = kv.split(%r/=/,2).last if kv
635
+
636
+ ### fallback to development
637
+ unless rails_env
638
+ rails_env = "development"
639
+ end
640
+
641
+ ### bootstrap
642
+ RAILS_ENV = rails_env
643
+ end
644
+ #
645
+ # ensure that rubygems is loaded
646
+ #
647
+ begin
648
+ require "rubygems"
649
+ rescue
650
+ 42
651
+ end
652
+ #
653
+ # load gems from plugin dir iff installed as plugin - otherwise load normally
654
+ #
655
+ if RAILS_ROOT and BJ_SCRIPT
656
+ =begin
657
+ dir = Gem.dir
658
+ path = Gem.path
659
+ gem_home = File.join RAILS_ROOT, "vendor", "plugins", "bj", "gem_home"
660
+ gem_path = [gem_home]
661
+ Gem.send :use_paths, gem_home, gem_path
662
+ gem "bj"
663
+ require "bj"
664
+ gem "main"
665
+ require "main"
666
+ =end
667
+ libdir = File.join(RAILS_ROOT, "vendor", "plugins", "bj", "lib")
668
+ $LOAD_PATH.unshift libdir
669
+ end
670
+ #
671
+ # hack of #to_s of STDERR/STDOUT for nice help messages
672
+ #
673
+ class << STDERR
674
+ def to_s() 'STDERR' end
675
+ end
676
+ class << STDOUT
677
+ def to_s() 'STDOUT' end
678
+ end
679
+ }