bj 0.0.1
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/HISTORY +2 -0
- data/README +130 -0
- data/TODO +3 -0
- data/bin/bj +478 -0
- data/gemspec.rb +29 -0
- data/install.rb +210 -0
- data/lib/bj.rb +79 -0
- data/lib/bj/api.rb +144 -0
- data/lib/bj/bj.rb +92 -0
- data/lib/bj/errors.rb +4 -0
- data/lib/bj/joblist.rb +112 -0
- data/lib/bj/logger.rb +50 -0
- data/lib/bj/runner.rb +285 -0
- data/lib/bj/stdext.rb +80 -0
- data/lib/bj/table.rb +388 -0
- data/lib/bj/util.rb +81 -0
- metadata +99 -0
data/gemspec.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
lib, version = File::basename(File::dirname(File::expand_path(__FILE__))).split %r/-/, 2
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
Gem::Specification::new do |spec|
|
5
|
+
$VERBOSE = nil
|
6
|
+
spec.name = lib
|
7
|
+
spec.version = version
|
8
|
+
spec.platform = Gem::Platform::RUBY
|
9
|
+
spec.summary = lib
|
10
|
+
|
11
|
+
spec.files = Dir::glob "**/**"
|
12
|
+
spec.executables = Dir::glob("bin/*").map{|exe| File::basename exe}
|
13
|
+
|
14
|
+
spec.require_path = "lib"
|
15
|
+
spec.autorequire = lib
|
16
|
+
|
17
|
+
spec.has_rdoc = File::exist? "doc"
|
18
|
+
spec.test_suite_file = "test/#{ lib }.rb" if File::directory? "test"
|
19
|
+
spec.add_dependency 'attributes', '>= 5.0.0'
|
20
|
+
spec.add_dependency 'main', '>= 2.6.0'
|
21
|
+
spec.add_dependency 'systemu', '>= 1.2.0'
|
22
|
+
spec.add_dependency 'orderedhash', '>= 0.0.3'
|
23
|
+
|
24
|
+
spec.extensions << "extconf.rb" if File::exists? "extconf.rb"
|
25
|
+
|
26
|
+
spec.author = "Ara T. Howard"
|
27
|
+
spec.email = "ara.t.howard@gmail.com"
|
28
|
+
spec.homepage = "http://codeforpeople.com/lib/ruby/#{ lib }/"
|
29
|
+
end
|
data/install.rb
ADDED
@@ -0,0 +1,210 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rbconfig'
|
3
|
+
require 'find'
|
4
|
+
require 'ftools'
|
5
|
+
require 'tempfile'
|
6
|
+
include Config
|
7
|
+
|
8
|
+
LIBDIR = "lib"
|
9
|
+
LIBDIR_MODE = 0644
|
10
|
+
|
11
|
+
BINDIR = "bin"
|
12
|
+
BINDIR_MODE = 0755
|
13
|
+
|
14
|
+
|
15
|
+
$srcdir = CONFIG["srcdir"]
|
16
|
+
$version = CONFIG["MAJOR"]+"."+CONFIG["MINOR"]
|
17
|
+
$libdir = File.join(CONFIG["libdir"], "ruby", $version)
|
18
|
+
$archdir = File.join($libdir, CONFIG["arch"])
|
19
|
+
$site_libdir = $:.find {|x| x =~ /site_ruby$/}
|
20
|
+
$bindir = CONFIG["bindir"] || CONFIG['BINDIR']
|
21
|
+
$ruby_install_name = CONFIG['ruby_install_name'] || CONFIG['RUBY_INSTALL_NAME'] || 'ruby'
|
22
|
+
$ruby_ext = CONFIG['EXEEXT'] || ''
|
23
|
+
$ruby = File.join($bindir, ($ruby_install_name + $ruby_ext))
|
24
|
+
|
25
|
+
if !$site_libdir
|
26
|
+
$site_libdir = File.join($libdir, "site_ruby")
|
27
|
+
elsif $site_libdir !~ %r/#{Regexp.quote($version)}/
|
28
|
+
$site_libdir = File.join($site_libdir, $version)
|
29
|
+
end
|
30
|
+
|
31
|
+
def install_rb(srcdir=nil, destdir=nil, mode=nil, bin=nil)
|
32
|
+
#{{{
|
33
|
+
path = []
|
34
|
+
dir = []
|
35
|
+
Find.find(srcdir) do |f|
|
36
|
+
next unless FileTest.file?(f)
|
37
|
+
next if (f = f[srcdir.length+1..-1]) == nil
|
38
|
+
next if (/CVS$/ =~ File.dirname(f))
|
39
|
+
next if f =~ %r/\.lnk/
|
40
|
+
path.push f
|
41
|
+
dir |= [File.dirname(f)]
|
42
|
+
end
|
43
|
+
for f in dir
|
44
|
+
next if f == "."
|
45
|
+
next if f == "CVS"
|
46
|
+
File::makedirs(File.join(destdir, f))
|
47
|
+
end
|
48
|
+
for f in path
|
49
|
+
next if (/\~$/ =~ f)
|
50
|
+
next if (/^\./ =~ File.basename(f))
|
51
|
+
unless bin
|
52
|
+
File::install(File.join(srcdir, f), File.join(destdir, f), mode, true)
|
53
|
+
else
|
54
|
+
from = File.join(srcdir, f)
|
55
|
+
to = File.join(destdir, f)
|
56
|
+
shebangify(from) do |sf|
|
57
|
+
$deferr.print from, " -> ", File::catname(from, to), "\n"
|
58
|
+
$deferr.printf "chmod %04o %s\n", mode, to
|
59
|
+
File::install(sf, to, mode, false)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
#}}}
|
64
|
+
end
|
65
|
+
def shebangify f
|
66
|
+
#{{{
|
67
|
+
open(f) do |fd|
|
68
|
+
buf = fd.read 42
|
69
|
+
if buf =~ %r/^\s*#\s*!.*ruby/o
|
70
|
+
ftmp = Tempfile::new("#{ $$ }_#{ File::basename(f) }")
|
71
|
+
begin
|
72
|
+
fd.rewind
|
73
|
+
ftmp.puts "#!#{ $ruby }"
|
74
|
+
while((buf = fd.read(8192)))
|
75
|
+
ftmp.write buf
|
76
|
+
end
|
77
|
+
ftmp.close
|
78
|
+
yield ftmp.path
|
79
|
+
ensure
|
80
|
+
ftmp.close!
|
81
|
+
end
|
82
|
+
else
|
83
|
+
yield f
|
84
|
+
end
|
85
|
+
end
|
86
|
+
#}}}
|
87
|
+
end
|
88
|
+
def ARGV.switch
|
89
|
+
#{{{
|
90
|
+
return nil if self.empty?
|
91
|
+
arg = self.shift
|
92
|
+
return nil if arg == '--'
|
93
|
+
if arg =~ /^-(.)(.*)/
|
94
|
+
return arg if $1 == '-'
|
95
|
+
raise 'unknown switch "-"' if $2.index('-')
|
96
|
+
self.unshift "-#{$2}" if $2.size > 0
|
97
|
+
"-#{$1}"
|
98
|
+
else
|
99
|
+
self.unshift arg
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
#}}}
|
103
|
+
end
|
104
|
+
def ARGV.req_arg
|
105
|
+
#{{{
|
106
|
+
self.shift || raise('missing argument')
|
107
|
+
#}}}
|
108
|
+
end
|
109
|
+
def linkify d, linked = []
|
110
|
+
#--{{{
|
111
|
+
if test ?d, d
|
112
|
+
versioned = Dir[ File::join(d, "*-[0-9].[0-9].[0-9].rb") ]
|
113
|
+
versioned.each do |v|
|
114
|
+
src, dst = v, v.gsub(%r/\-[\d\.]+\.rb$/, '.rb')
|
115
|
+
lnk = nil
|
116
|
+
begin
|
117
|
+
if test ?l, dst
|
118
|
+
lnk = "#{ dst }.lnk"
|
119
|
+
puts "#{ dst } -> #{ lnk }"
|
120
|
+
File::rename dst, lnk
|
121
|
+
end
|
122
|
+
unless test ?e, dst
|
123
|
+
puts "#{ src } -> #{ dst }"
|
124
|
+
File::copy src, dst
|
125
|
+
linked << dst
|
126
|
+
end
|
127
|
+
ensure
|
128
|
+
if lnk
|
129
|
+
at_exit do
|
130
|
+
puts "#{ lnk } -> #{ dst }"
|
131
|
+
File::rename lnk, dst
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
linked
|
138
|
+
#--}}}
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
#
|
143
|
+
# main program
|
144
|
+
#
|
145
|
+
|
146
|
+
libdir = $site_libdir
|
147
|
+
bindir = $bindir
|
148
|
+
no_linkify = false
|
149
|
+
linked = nil
|
150
|
+
help = false
|
151
|
+
|
152
|
+
usage = <<-usage
|
153
|
+
#{ File::basename $0 }
|
154
|
+
-d, --destdir <destdir>
|
155
|
+
-l, --libdir <libdir>
|
156
|
+
-b, --bindir <bindir>
|
157
|
+
-r, --ruby <ruby>
|
158
|
+
-n, --no_linkify
|
159
|
+
-s, --sudo
|
160
|
+
-h, --help
|
161
|
+
usage
|
162
|
+
|
163
|
+
begin
|
164
|
+
while switch = ARGV.switch
|
165
|
+
case switch
|
166
|
+
when '-d', '--destdir'
|
167
|
+
libdir = ARGV.req_arg
|
168
|
+
when '-l', '--libdir'
|
169
|
+
libdir = ARGV.req_arg
|
170
|
+
when '-b', '--bindir'
|
171
|
+
bindir = ARGV.req_arg
|
172
|
+
when '-r', '--ruby'
|
173
|
+
$ruby = ARGV.req_arg
|
174
|
+
when '-n', '--no_linkify'
|
175
|
+
no_linkify = true
|
176
|
+
when '-s', '--sudo'
|
177
|
+
sudo = 'sudo'
|
178
|
+
when '-h', '--help'
|
179
|
+
help = true
|
180
|
+
else
|
181
|
+
raise "unknown switch #{switch.dump}"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
rescue
|
185
|
+
STDERR.puts $!.to_s
|
186
|
+
STDERR.puts usage
|
187
|
+
exit 1
|
188
|
+
end
|
189
|
+
|
190
|
+
if help
|
191
|
+
STDOUT.puts usage
|
192
|
+
exit
|
193
|
+
end
|
194
|
+
|
195
|
+
system "#{ sudo } #{ $ruby } pre-install.rb" if test(?s, 'pre-install.rb')
|
196
|
+
|
197
|
+
unless no_linkify
|
198
|
+
linked = linkify('lib') + linkify('bin')
|
199
|
+
end
|
200
|
+
|
201
|
+
system "#{ $ruby } extconf.rb && make && #{ sudo } make install" if test(?s, 'extconf.rb')
|
202
|
+
|
203
|
+
install_rb(LIBDIR, libdir, LIBDIR_MODE)
|
204
|
+
install_rb(BINDIR, bindir, BINDIR_MODE, bin=true)
|
205
|
+
|
206
|
+
if linked
|
207
|
+
linked.each{|path| File::rm_f path}
|
208
|
+
end
|
209
|
+
|
210
|
+
system "#{ sudo } #{ $ruby } post-install.rb" if test(?s, 'post-install.rb')
|
data/lib/bj.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
unless defined? Bj
|
2
|
+
class Bj
|
3
|
+
#
|
4
|
+
# constants and associated attrs
|
5
|
+
#
|
6
|
+
Bj::VERSION = "0.0.1" unless
|
7
|
+
defined? Bj::VERSION
|
8
|
+
def self.version() Bj::VERSION end
|
9
|
+
|
10
|
+
Bj::LIBDIR = File.expand_path(File::join(File.dirname(__FILE__), "bj")) + File::SEPARATOR unless
|
11
|
+
defined? Bj::LIBDIR
|
12
|
+
def self.libdir(*value)
|
13
|
+
unless value.empty?
|
14
|
+
File.join libdir, *value
|
15
|
+
else
|
16
|
+
Bj::LIBDIR
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module EXIT
|
21
|
+
SUCCESS = 0
|
22
|
+
FAILURE = 1
|
23
|
+
WARNING = 42
|
24
|
+
end
|
25
|
+
#
|
26
|
+
# built-in
|
27
|
+
#
|
28
|
+
require "socket"
|
29
|
+
require "yaml"
|
30
|
+
require "thread"
|
31
|
+
require "rbconfig"
|
32
|
+
#
|
33
|
+
# bootstrap rubygems
|
34
|
+
#
|
35
|
+
begin
|
36
|
+
require "rubygems"
|
37
|
+
rescue LoadError
|
38
|
+
42
|
39
|
+
end
|
40
|
+
#
|
41
|
+
# rubyforge/remote
|
42
|
+
#
|
43
|
+
require "active_record"
|
44
|
+
#
|
45
|
+
# rubyforge/remote or local/lib
|
46
|
+
#
|
47
|
+
%w[ attributes main systemu orderedhash ].each do |lib|
|
48
|
+
begin
|
49
|
+
require lib
|
50
|
+
rescue
|
51
|
+
require libdir(lib)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
#
|
55
|
+
# local
|
56
|
+
#
|
57
|
+
load libdir("stdext.rb")
|
58
|
+
load libdir("util.rb")
|
59
|
+
load libdir("errors.rb")
|
60
|
+
load libdir("logger.rb")
|
61
|
+
load libdir("bj.rb")
|
62
|
+
load libdir("joblist.rb")
|
63
|
+
load libdir("table.rb")
|
64
|
+
load libdir("runner.rb")
|
65
|
+
load libdir("api.rb")
|
66
|
+
#
|
67
|
+
# an imperfect reloading hook - because neither rails' plugins nor gems provide one, sigh...
|
68
|
+
#
|
69
|
+
def self.reload!
|
70
|
+
::Object.module_eval do
|
71
|
+
remove_const :Bj rescue nil
|
72
|
+
remove_const :BackgroundJob rescue nil
|
73
|
+
end
|
74
|
+
load __FILE__ rescue nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
BackgroundJob = Bj
|
79
|
+
end
|
data/lib/bj/api.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
class Bj
|
2
|
+
#
|
3
|
+
# the api exposes nearly all the bj code you'll likely need, with the
|
4
|
+
# exception of accessing the job table for searching, which is done using
|
5
|
+
#
|
6
|
+
# eg.
|
7
|
+
#
|
8
|
+
# Bj.table.job.find :all
|
9
|
+
#
|
10
|
+
module API
|
11
|
+
#
|
12
|
+
# submit jobs for background processing. 'jobs' can be a string or array of
|
13
|
+
# strings. options are applied to each job in the 'jobs', and the list of
|
14
|
+
# submitted jobs is always returned. options (string or symbol) can be
|
15
|
+
#
|
16
|
+
# :rails_env => production|development|key_in_database_yml
|
17
|
+
# when given this keyword causes bj to submit jobs to the
|
18
|
+
# specified database. default is RAILS_ENV.
|
19
|
+
#
|
20
|
+
# :priority => any number, including negative ones. default is zero.
|
21
|
+
#
|
22
|
+
# :tag => a tag added to the job. simply makes searching easier.
|
23
|
+
#
|
24
|
+
# :env => a hash specifying any additional environment vars the background
|
25
|
+
# process should have.
|
26
|
+
#
|
27
|
+
# :stdin => any stdin the background process should have.
|
28
|
+
#
|
29
|
+
# eg:
|
30
|
+
#
|
31
|
+
# jobs = Bj.submit 'echo foobar', :tag => 'simple job'
|
32
|
+
#
|
33
|
+
# jobs = Bj.submit '/bin/cat', :stdin => 'in the hat'
|
34
|
+
#
|
35
|
+
# jobs = Bj.submit './script/runner ./scripts/a.rb', :rails_env => 'production'
|
36
|
+
#
|
37
|
+
# when jobs are run, they are run in RAILS_ROOT. various attributes are
|
38
|
+
# available *only* once the job has finished. you can check whether or not
|
39
|
+
# a job is finished by using the #finished method, which simple does a
|
40
|
+
# reload and checks to see if the exit_status is non-nil.
|
41
|
+
#
|
42
|
+
# eg:
|
43
|
+
#
|
44
|
+
# jobs = Bj.submit list_of_jobs, :tag => 'important'
|
45
|
+
# ...
|
46
|
+
#
|
47
|
+
# jobs.each do |job|
|
48
|
+
# if job.finished?
|
49
|
+
# p job.exit_status
|
50
|
+
# p job.stdout
|
51
|
+
# p job.stderr
|
52
|
+
# end
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
#
|
56
|
+
def submit jobs, options = {}, &block
|
57
|
+
options.to_options!
|
58
|
+
Bj.transaction(options) do
|
59
|
+
table.job.submit jobs, options, &block
|
60
|
+
end
|
61
|
+
ensure
|
62
|
+
Bj.runner.tickle unless options[:no_tickle]
|
63
|
+
end
|
64
|
+
#
|
65
|
+
# this method changes the context under which bj is operating. a context is
|
66
|
+
# a RAILS_ENV. the method accepts a block and it used to alter the
|
67
|
+
# behaviour of the bj lib on a global scale such that all operations,
|
68
|
+
# spawning of background runnner processes, etc, occur in that context.
|
69
|
+
#
|
70
|
+
# eg:
|
71
|
+
#
|
72
|
+
# Bj.in :production do
|
73
|
+
# Bj.submit './script/runner ./scripts/facebook_notification.rb'
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# Bj.in :development do
|
77
|
+
# Bj.submit 'does_this_eat_memory.exe'
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
def in rails_env = Bj.rails_env, &block
|
81
|
+
transaction(:rails_env => rails_env.to_s, &block)
|
82
|
+
end
|
83
|
+
#
|
84
|
+
# list simply returns a list of all jobs in the job table
|
85
|
+
#
|
86
|
+
def list options = {}, &block
|
87
|
+
Bj.transaction(options) do
|
88
|
+
table.job.find(:all, options)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
#
|
92
|
+
#
|
93
|
+
#
|
94
|
+
def run options = {}
|
95
|
+
runner.run options
|
96
|
+
end
|
97
|
+
#
|
98
|
+
# generate a migration and migrate a database (production/development/etc)
|
99
|
+
#
|
100
|
+
def setup options = {}
|
101
|
+
options.to_options!
|
102
|
+
chroot do
|
103
|
+
generate_migration options
|
104
|
+
migrate options
|
105
|
+
end
|
106
|
+
end
|
107
|
+
#
|
108
|
+
# generate_migration, suprisingly, generates the single migration needed for
|
109
|
+
# bj. you'll notice the the migration is very short as the migration
|
110
|
+
# classes themselves are inner classes of the respective bj table class.
|
111
|
+
# see lib/bj/table.rb for details.
|
112
|
+
#
|
113
|
+
def generate_migration options = {}
|
114
|
+
options.to_options!
|
115
|
+
chroot do
|
116
|
+
before = Dir.glob "./db/migrate/*"
|
117
|
+
util.spawn "./script/generate migration BjMigration", options rescue nil
|
118
|
+
after = Dir.glob "./db/migrate/*"
|
119
|
+
candidates = after - before
|
120
|
+
case candidates.size
|
121
|
+
when 0
|
122
|
+
false
|
123
|
+
when 1
|
124
|
+
generated = candidates.first
|
125
|
+
open(generated, "w"){|fd| fd.puts Bj.table.migration_code}
|
126
|
+
Bj.logger.info{ "generated <#{ generated }>" }
|
127
|
+
generated
|
128
|
+
else
|
129
|
+
raise "ambiguous migration <#{ candidates.inspect }>"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
#
|
134
|
+
# migrate a database (production|development|etc)
|
135
|
+
#
|
136
|
+
def migrate options = {}
|
137
|
+
options.to_options!
|
138
|
+
chroot do
|
139
|
+
util.spawn "rake RAILS_ENV=#{ Bj.rails_env } db:migrate", options
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
send :extend, API
|
144
|
+
end
|