bj_fixed_for_rails3 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/bj/table.rb ADDED
@@ -0,0 +1,384 @@
1
+ class Bj
2
+ #
3
+ # table base class
4
+ #
5
+ class Table < ActiveRecord::Base
6
+ module ClassMethods
7
+ attribute("list"){ Array.new }
8
+ attribute("migration"){}
9
+
10
+ def migration_code classname = "BjMigration"
11
+ <<-code
12
+ class #{ classname } < ActiveRecord::Migration
13
+ def self.up
14
+ Bj::Table.each{|table| table.up}
15
+ end
16
+ def self.down
17
+ Bj::Table.reverse_each{|table| table.down}
18
+ end
19
+ end
20
+ code
21
+ end
22
+
23
+ def up
24
+ migration_class.up
25
+ end
26
+
27
+ def down
28
+ migration_class.down
29
+ end
30
+
31
+ def migration_class
32
+ table = self
33
+ @migration_class ||=
34
+ Class.new(ActiveRecord::Migration) do
35
+ sc =
36
+ class << self
37
+ self
38
+ end
39
+ sc.module_eval{ attribute :table => table }
40
+ sc.module_eval &table.migration
41
+ end
42
+ end
43
+
44
+ def content_column_names
45
+ @content_column_names = content_columns.map{|column| column.name}
46
+ end
47
+
48
+ def create_hash_for options
49
+ options.to_options!
50
+ hash = {}
51
+ content_column_names.each do |key|
52
+ key = key.to_s.to_sym
53
+ hash[key] = options[key]
54
+ end
55
+ hash
56
+ end
57
+
58
+ def each *a, &b
59
+ list.each *a, &b
60
+ end
61
+
62
+ def reverse_each *a, &b
63
+ list.reverse.each *a, &b
64
+ end
65
+ end
66
+ send :extend, ClassMethods
67
+
68
+ module InstanceMethods
69
+ def to_hash
70
+ oh = OrderedHash.new
71
+ self.class.content_column_names.each{|c| oh[c] = self[c]}
72
+ oh
73
+ end
74
+ end
75
+ send :include, InstanceMethods
76
+
77
+ module RecursivelyInherited
78
+ def inherited other
79
+ super
80
+ ensure
81
+ (Table.list << other).uniq!
82
+ basename = other.name.split(%r/::/).last.underscore
83
+ Table.singleton_class{ attribute basename => other }
84
+ other.send :extend, RecursivelyInherited
85
+ end
86
+ end
87
+ send :extend, RecursivelyInherited
88
+
89
+ #
90
+ # table classes
91
+ #
92
+ class Job < Table
93
+ set_table_name "bj_job"
94
+ set_primary_key "#{ table_name }_id"
95
+
96
+ migration {
97
+ define_method :up do
98
+ create_table table.table_name, :primary_key => table.primary_key, :force => true do |t|
99
+ t.column "command" , :text
100
+
101
+ t.column "state" , :text
102
+ t.column "priority" , :integer
103
+ t.column "tag" , :text
104
+ t.column "is_restartable" , :integer
105
+
106
+ t.column "submitter" , :text
107
+ t.column "runner" , :text
108
+ t.column "pid" , :integer
109
+
110
+ t.column "submitted_at" , :datetime
111
+ t.column "started_at" , :datetime
112
+ t.column "finished_at" , :datetime
113
+
114
+ t.column "env" , :text
115
+ t.column "stdin" , :text
116
+ t.column "stdout" , :text
117
+ t.column "stderr" , :text
118
+ t.column "exit_status" , :integer
119
+ end
120
+ end
121
+
122
+ define_method :down do
123
+ drop_table table.table_name
124
+ end
125
+ }
126
+
127
+ module ClassMethods
128
+ def submit jobs, options = {}, &block
129
+ jobs = Joblist.for jobs, options
130
+ returned = []
131
+ transaction do
132
+ jobs.each do |job|
133
+ job = create_hash_for(job.reverse_merge(submit_defaults))
134
+ job = create! job
135
+ returned << (block ? block.call(job) : job)
136
+ end
137
+ end
138
+ returned
139
+ end
140
+
141
+ def submit_defaults
142
+ {
143
+ :state => "pending",
144
+ :priority => 0,
145
+ :tag => "",
146
+ :is_restartable => true,
147
+ :submitter => Bj.hostname,
148
+ :submitted_at => Time.now,
149
+ }
150
+ end
151
+ end
152
+ send :extend, ClassMethods
153
+
154
+ module InstanceMethods
155
+ def title
156
+ "job[#{ id }](#{ command })"
157
+ end
158
+ def finished
159
+ reload
160
+ exit_status
161
+ end
162
+ alias_method "finished?", "finished"
163
+ end
164
+ send :include, InstanceMethods
165
+ end
166
+
167
+ class JobArchive < Job
168
+ set_table_name "bj_job_archive"
169
+ set_primary_key "#{ table_name }_id"
170
+
171
+ migration {
172
+ define_method(:up) do
173
+ create_table table.table_name, :primary_key => table.primary_key, :force => true do |t|
174
+ t.column "command" , :text
175
+
176
+ t.column "state" , :text
177
+ t.column "priority" , :integer
178
+ t.column "tag" , :text
179
+ t.column "is_restartable" , :integer
180
+
181
+ t.column "submitter" , :text
182
+ t.column "runner" , :text
183
+ t.column "pid" , :integer
184
+
185
+ t.column "submitted_at" , :datetime
186
+ t.column "started_at" , :datetime
187
+ t.column "finished_at" , :datetime
188
+ t.column "archived_at" , :datetime
189
+
190
+ t.column "env" , :text
191
+ t.column "stdin" , :text
192
+ t.column "stdout" , :text
193
+ t.column "stderr" , :text
194
+ t.column "exit_status" , :integer
195
+ end
196
+ end
197
+
198
+ define_method(:down) do
199
+ drop_table table.table_name
200
+ end
201
+ }
202
+ end
203
+
204
+ # TODO - initialize with a set of global defaults and fallback to those on perhaps '* * key'
205
+ class Config < Table
206
+ set_table_name "bj_config"
207
+ set_primary_key "#{ table_name }_id"
208
+
209
+ migration {
210
+ define_method(:up) do
211
+ create_table table.table_name, :primary_key => table.primary_key, :force => true do |t|
212
+ t.column "hostname" , :text
213
+ t.column "key" , :text
214
+ t.column "value" , :text
215
+ t.column "cast" , :text
216
+ end
217
+
218
+ begin
219
+ add_index table.table_name, %w[ hostname key ], :unique => true
220
+ rescue Exception
221
+ STDERR.puts "WARNING: your database does not support unique indexes on text fields!?"
222
+ end
223
+ end
224
+
225
+ define_method(:down) do
226
+ begin
227
+ remove_index table.table_name, :column => %w[ hostname key ]
228
+ rescue Exception
229
+ end
230
+ drop_table table.table_name
231
+ end
232
+ }
233
+
234
+ module ClassMethods
235
+ def [] key
236
+ get key
237
+ end
238
+
239
+ def get key, options = {}
240
+ transaction do
241
+ options.to_options!
242
+ hostname = options[:hostname] || Bj.hostname
243
+ record = find :first, :conditions => conditions(:key => key, :hostname => hostname)
244
+ record ? record.value : default_for(key)
245
+ end
246
+ end
247
+
248
+ def conditions options = {}
249
+ options.to_options!
250
+ options.reverse_merge!(
251
+ :hostname => Bj.hostname
252
+ )
253
+ options
254
+ end
255
+
256
+ def default_for key
257
+ record = find :first, :conditions => conditions(:key => key, :hostname => '*')
258
+ record ? record.value : nil
259
+ end
260
+
261
+ def []= key, value
262
+ set key, value
263
+ end
264
+
265
+ def set key, value, options = {}
266
+ transaction do
267
+ options.to_options!
268
+ hostname = options[:hostname] || Bj.hostname
269
+ record = find :first, :conditions => conditions(:key => key, :hostname => hostname), :lock => true
270
+ cast = options[:cast] || cast_for(value)
271
+ key = key.to_s
272
+ value = value.to_s
273
+ if record
274
+ record["value"] = value
275
+ record["cast"] = cast
276
+ record.save!
277
+ else
278
+ create! :hostname => hostname, :key => key, :value => value, :cast => cast
279
+ end
280
+ value
281
+ end
282
+ end
283
+
284
+ def delete key
285
+ transaction do
286
+ record = find :first, :conditions => conditions(:key => key), :lock => true
287
+ if record
288
+ record.destroy
289
+ record
290
+ else
291
+ nil
292
+ end
293
+ end
294
+ end
295
+
296
+ def has_key? key
297
+ record = find :first, :conditions => conditions(:key => key)
298
+ record ? record : false
299
+ end
300
+ alias_method "has_key", "has_key?"
301
+
302
+ def keys
303
+ find(:all, :conditions => conditions).map(&:key)
304
+ end
305
+
306
+ def values
307
+ find(:all, :conditions => conditions).map(&:value)
308
+ end
309
+
310
+ def for options = {}
311
+ oh = OrderedHash.new
312
+ find(:all, :conditions => conditions(options)).each do |record|
313
+ oh[record.key] = record.value
314
+ end
315
+ oh
316
+ end
317
+
318
+ def cast_for value
319
+ case value
320
+ when TrueClass, FalseClass
321
+ 'to_bool'
322
+ when NilClass
323
+ 'to_nil'
324
+ when Fixnum, Bignum
325
+ 'to_i'
326
+ when Float
327
+ 'to_f'
328
+ when Time
329
+ 'to_time'
330
+ when Symbol
331
+ 'to_sym'
332
+ else
333
+ case value.to_s
334
+ when %r/^\d+$/
335
+ 'to_i'
336
+ when %r/^\d+\.\d+$/
337
+ 'to_f'
338
+ when %r/^nil$|^$/
339
+ 'to_nil'
340
+ when %r/^true|false$/
341
+ 'to_bool'
342
+ else
343
+ 'to_s'
344
+ end
345
+ end
346
+ end
347
+
348
+ def casts
349
+ @casts ||= {
350
+ 'to_bool' => lambda do |value|
351
+ value.to_s =~ %r/^true$/i ? true : false
352
+ end,
353
+ 'to_i' => lambda do |value|
354
+ Integer value.to_s.gsub(%r/^(-)?0*/,'\1')
355
+ end,
356
+ 'to_f' => lambda do |value|
357
+ Float value.to_s.gsub(%r/^0*/,'')
358
+ end,
359
+ 'to_time' => lambda do |value|
360
+ Time.parse(value.to_s)
361
+ end,
362
+ 'to_sym' => lambda do |value|
363
+ value.to_s.to_sym
364
+ end,
365
+ 'to_nil' => lambda do |value|
366
+ value.to_s =~ %r/^nil$|^$/i ? nil : value.to_s
367
+ end,
368
+ 'to_s' => lambda do |value|
369
+ value.to_s
370
+ end,
371
+ }
372
+ end
373
+ end
374
+ send :extend, ClassMethods
375
+
376
+ module InstanceMethods
377
+ def value
378
+ self.class.casts[cast][self["value"]]
379
+ end
380
+ end
381
+ send :include, InstanceMethods
382
+ end
383
+ end
384
+ end
data/lib/bj/util.rb ADDED
@@ -0,0 +1,111 @@
1
+ class Bj
2
+ module Util
3
+ module ModuleMethods
4
+ def constant_get const, &block
5
+ begin
6
+ ancestors = const.split(%r/::/)
7
+ parent = Object
8
+ while((child = ancestors.shift))
9
+ klass = parent.const_get child
10
+ parent = klass
11
+ end
12
+ klass
13
+ rescue
14
+ block ? block.call : raise
15
+ end
16
+ end
17
+
18
+ def const_or_env const, &block
19
+ constant_get(const){ ENV[const] || block.call }
20
+ end
21
+
22
+ def spawn cmd, options = {}
23
+ options.to_options!
24
+ logger = options.has_key?(:logger) ? options[:logger] : Bj.logger
25
+ logger.info{ "cmd <#{ cmd }>" } if logger
26
+ status = systemu cmd, 1=>(stdout=""), 2=>(stderr="")
27
+ logger.info{ "status <#{ status.exitstatus }>" } if logger
28
+ status.exitstatus.zero? or raise "#{ cmd.inspect } failed with #{ $?.inspect }"
29
+ [ stdout, stderr ]
30
+ end
31
+
32
+ def start *a
33
+ q = Queue.new
34
+ thread = Thread.new do
35
+ Thread.current.abort_on_exception = true
36
+ systemu(*a){|pid| q << pid}
37
+ end
38
+ pid = q.pop
39
+ thread.singleton_class{ define_method(:pid){ pid } }
40
+ thread
41
+ end
42
+
43
+ def alive pid
44
+ return false unless pid
45
+ pid = Integer pid.to_s
46
+ Process::kill 0, pid
47
+ true
48
+ rescue Errno::ESRCH, Errno::EPERM
49
+ false
50
+ end
51
+ alias_method "alive?", "alive"
52
+
53
+ def which_ruby
54
+ c = ::Config::CONFIG
55
+ ruby = File::join(c['bindir'], c['ruby_install_name']) << c['EXEEXT']
56
+ raise "ruby @ #{ ruby } not executable!?" unless test(?e, ruby)
57
+ ruby
58
+ end
59
+
60
+ def which_rake
61
+ tmp = Tempfile.new Process.pid
62
+ tmp.write "task(:foobar){ puts 42 }"
63
+ tmp.close
64
+ bat = spawn("rake.bat -f #{ tmp.path.inspect } foobar", :logger => false) rescue false
65
+ bat ? "rake.bat" : "rake"
66
+ ensure
67
+ tmp.close! rescue nil
68
+ end
69
+
70
+ def ipc_signals_supported
71
+ @ipc_signals_supported ||=
72
+ IO.popen 'ruby', 'r+' do |ruby|
73
+ pid = ruby.pid
74
+ begin
75
+ Process.kill 'TERM', pid
76
+ true
77
+ rescue Exception
78
+ false
79
+ end
80
+ end
81
+ end
82
+ alias_method "ipc_signals_supported?", "ipc_signals_supported"
83
+
84
+ def find_script basename
85
+ path = ENV["PATH"] || ENV["path"] || Bj.default_path
86
+ raise "no env PATH" unless path
87
+ path = path.split File::PATH_SEPARATOR
88
+ path.unshift File.join(Bj.rails_root, "script")
89
+ path.each do |directory|
90
+ script = File.join directory, basename
91
+ return File.expand_path(script) if(test(?s, script) and test(?r, script))
92
+ end
93
+ raise "no #{ basename } found in #{ path.inspect }"
94
+ end
95
+
96
+ def valid_rails_root root = ".", expected = %w[ config script app ]
97
+ directories = expected.flatten.compact.map{|dir| dir.to_s}
98
+ directories.all?{|dir| test(?d, File.join(root, dir))}
99
+ end
100
+ alias_method "valid_rails_root?", "valid_rails_root"
101
+
102
+ def emsg e
103
+ m = e.message rescue ""
104
+ c = e.class rescue Exception
105
+ b = e.backtrace.join("\n") rescue ""
106
+ "#{ m }(#{ c })\n#{ b }"
107
+ end
108
+ end
109
+ send :extend, ModuleMethods
110
+ end
111
+ end
data/lib/bj.rb ADDED
@@ -0,0 +1,88 @@
1
+ unless defined? Bj
2
+ class Bj
3
+ #
4
+ # constants and associated attrs
5
+ #
6
+ Bj::VERSION = "1.0.1" #unless defined? Bj::VERSION
7
+ def self.version() Bj::VERSION end
8
+
9
+ Bj::LIBDIR = File.expand_path(File::join(File.dirname(__FILE__), "bj")) + File::SEPARATOR unless
10
+ defined? Bj::LIBDIR
11
+ def self.libdir(*value)
12
+ unless value.empty?
13
+ File.join libdir, *value
14
+ else
15
+ Bj::LIBDIR
16
+ end
17
+ end
18
+
19
+ module EXIT
20
+ SUCCESS = 0
21
+ FAILURE = 1
22
+ WARNING = 42
23
+ end
24
+ #
25
+ # built-in
26
+ #
27
+ require "socket"
28
+ require "yaml"
29
+ require "thread"
30
+ require "rbconfig"
31
+ require "set"
32
+ require "erb"
33
+ require "tempfile"
34
+ require "logger"
35
+ #
36
+ # bootstrap rubygems
37
+ #
38
+ begin
39
+ require "rubygems"
40
+ rescue LoadError
41
+ 42
42
+ end
43
+ #
44
+ # rubyforge/remote
45
+ #
46
+ require "active_record"
47
+ #
48
+ # rubyforge/remote or local/lib
49
+ #
50
+ #%w[ attributes systemu orderedhash ].each do |lib|
51
+ %w[ systemu orderedhash ].each do |lib|
52
+ begin
53
+ require lib
54
+ rescue
55
+ require libdir(lib)
56
+ end
57
+ end
58
+ #
59
+ # local
60
+ #
61
+ load libdir("attributes.rb")
62
+ load libdir("stdext.rb")
63
+ load libdir("util.rb")
64
+ load libdir("errors.rb")
65
+ load libdir("logger.rb")
66
+ load libdir("bj.rb")
67
+ load libdir("joblist.rb")
68
+ load libdir("table.rb")
69
+ load libdir("runner.rb")
70
+ load libdir("api.rb")
71
+ #
72
+ # an imperfect reloading hook - because neither rails' plugins nor gems provide one, sigh...
73
+ #
74
+ def self.reload!
75
+ background = nil
76
+ ::Object.module_eval do
77
+ background = Bj.runner.background
78
+ remove_const :Bj rescue nil
79
+ remove_const :BackgroundJob rescue nil
80
+ end
81
+ returned = load __FILE__ rescue nil
82
+ Bj.runner.background = background if background
83
+ returned
84
+ end
85
+ end
86
+
87
+ BackgroundJob = Bj
88
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bj_fixed_for_rails3
3
+ version: !ruby/object:Gem::Version
4
+ hash: 19
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 2
10
+ version: 1.0.2
11
+ platform: ruby
12
+ authors:
13
+ - Ara T. Howard
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-12-15 00:00:00 -08:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: main
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 23
30
+ segments:
31
+ - 2
32
+ - 6
33
+ - 0
34
+ version: 2.6.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: systemu
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 31
46
+ segments:
47
+ - 1
48
+ - 2
49
+ - 0
50
+ version: 1.2.0
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: orderedhash
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 25
62
+ segments:
63
+ - 0
64
+ - 0
65
+ - 3
66
+ version: 0.0.3
67
+ type: :runtime
68
+ version_requirements: *id003
69
+ description:
70
+ email: ara.t.howard@gmail.com
71
+ executables:
72
+ - bj
73
+ extensions: []
74
+
75
+ extra_rdoc_files: []
76
+
77
+ files:
78
+ - bin/bj
79
+ - bj-1.0.2.gem
80
+ - gemspec.rb
81
+ - HISTORY
82
+ - install.rb
83
+ - lib/bj/api.rb
84
+ - lib/bj/attributes.rb
85
+ - lib/bj/bj.rb
86
+ - lib/bj/errors.rb
87
+ - lib/bj/joblist.rb
88
+ - lib/bj/logger.rb
89
+ - lib/bj/runner.rb
90
+ - lib/bj/stdext.rb
91
+ - lib/bj/table.rb
92
+ - lib/bj/util.rb
93
+ - lib/bj.rb
94
+ - README
95
+ - TODO
96
+ has_rdoc: true
97
+ homepage: http://codeforpeople.com/lib/ruby/bj_fixed_for_rails3/
98
+ licenses: []
99
+
100
+ post_install_message:
101
+ rdoc_options: []
102
+
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ hash: 3
111
+ segments:
112
+ - 0
113
+ version: "0"
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ hash: 3
120
+ segments:
121
+ - 0
122
+ version: "0"
123
+ requirements: []
124
+
125
+ rubyforge_project: codeforpeople
126
+ rubygems_version: 1.3.7
127
+ signing_key:
128
+ specification_version: 3
129
+ summary: bj_fixed_for_rails3
130
+ test_files: []
131
+