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/lib/bj/stdext.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
class Hash
|
2
|
+
begin
|
3
|
+
method "to_options"
|
4
|
+
rescue
|
5
|
+
def to_options
|
6
|
+
inject(Hash.new){|h, kv| h.update kv.first.to_s.to_sym => kv.last}
|
7
|
+
end
|
8
|
+
def to_options!
|
9
|
+
replace to_options
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
begin
|
14
|
+
method "to_string_options"
|
15
|
+
rescue
|
16
|
+
def to_string_options
|
17
|
+
inject(Hash.new){|h, kv| h.update kv.first.to_s => kv.last}
|
18
|
+
end
|
19
|
+
def to_string_options!
|
20
|
+
replace to_string_options
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
begin
|
25
|
+
method "reverse_merge"
|
26
|
+
rescue
|
27
|
+
def reverse_merge other
|
28
|
+
other.merge self
|
29
|
+
end
|
30
|
+
def reverse_merge! other
|
31
|
+
replace reverse_merge(other)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Object
|
37
|
+
begin
|
38
|
+
method "returning"
|
39
|
+
rescue
|
40
|
+
def returning value, &block
|
41
|
+
block.call value
|
42
|
+
value
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class Object
|
48
|
+
def singleton_class &block
|
49
|
+
@singleton_class ||=
|
50
|
+
class << self
|
51
|
+
self
|
52
|
+
end
|
53
|
+
block ? @singleton_class.module_eval(&block) : @singleton_class
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class Module
|
58
|
+
Instance_Methods = instance_method "instance_methods" unless defined? Instance_Methods
|
59
|
+
def instance_methods &block
|
60
|
+
block ? module_eval(&block) : Instance_Methods.bind(self).call
|
61
|
+
end
|
62
|
+
def module_methods &block
|
63
|
+
block ? singleton_class(&block) : singleton_methods
|
64
|
+
end
|
65
|
+
alias_method "class_methods", "module_methods"
|
66
|
+
end
|
67
|
+
|
68
|
+
class String
|
69
|
+
begin
|
70
|
+
method 'underscore'
|
71
|
+
rescue
|
72
|
+
def underscore
|
73
|
+
gsub(/::/, '/').
|
74
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
75
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
76
|
+
tr("-", "_").
|
77
|
+
downcase
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/bj/table.rb
ADDED
@@ -0,0 +1,388 @@
|
|
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 generate_migration
|
11
|
+
before = Dir.glob "./db/migrate/*"
|
12
|
+
Util.spawn "./script/generate migration BjMigration"
|
13
|
+
after = Dir.glob "./db/migrate/*"
|
14
|
+
candidates = after - before
|
15
|
+
case candidates.size
|
16
|
+
when 0
|
17
|
+
false
|
18
|
+
when 1
|
19
|
+
generated = candidates.first
|
20
|
+
open(generated, "w"){|fd| fd.puts migration_code}
|
21
|
+
generated
|
22
|
+
else
|
23
|
+
raise "ambiguous migration <#{ candidates.inspect }>"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def migration_code
|
28
|
+
<<-code
|
29
|
+
class BjMigration < ActiveRecord::Migration
|
30
|
+
def self.up
|
31
|
+
Bj::Table.each{|table| table.up}
|
32
|
+
end
|
33
|
+
def self.down
|
34
|
+
Bj::Table.reverse_each{|table| table.down}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
code
|
38
|
+
end
|
39
|
+
|
40
|
+
def up
|
41
|
+
migration_class.up
|
42
|
+
end
|
43
|
+
|
44
|
+
def down
|
45
|
+
migration_class.down
|
46
|
+
end
|
47
|
+
|
48
|
+
def migration_class
|
49
|
+
table = self
|
50
|
+
@migration_class ||=
|
51
|
+
Class.new(ActiveRecord::Migration) do
|
52
|
+
sc =
|
53
|
+
class << self
|
54
|
+
self
|
55
|
+
end
|
56
|
+
sc.module_eval{ attribute :table => table }
|
57
|
+
sc.module_eval &table.migration
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def content_column_names
|
62
|
+
@content_column_names = content_columns.map{|column| column.name}
|
63
|
+
end
|
64
|
+
|
65
|
+
def create_hash_for options
|
66
|
+
options.to_options!
|
67
|
+
hash = {}
|
68
|
+
content_column_names.each do |key|
|
69
|
+
key = key.to_s.to_sym
|
70
|
+
hash[key] = options[key]
|
71
|
+
end
|
72
|
+
hash
|
73
|
+
end
|
74
|
+
|
75
|
+
def each *a, &b
|
76
|
+
list.each *a, &b
|
77
|
+
end
|
78
|
+
|
79
|
+
def reverse_each *a, &b
|
80
|
+
list.reverse.each *a, &b
|
81
|
+
end
|
82
|
+
end
|
83
|
+
send :extend, ClassMethods
|
84
|
+
|
85
|
+
module InstanceMethods
|
86
|
+
def to_hash
|
87
|
+
oh = OrderedHash.new
|
88
|
+
self.class.content_column_names.each{|c| oh[c] = self[c]}
|
89
|
+
oh
|
90
|
+
end
|
91
|
+
end
|
92
|
+
send :include, InstanceMethods
|
93
|
+
|
94
|
+
module RecursivelyInherited
|
95
|
+
def inherited other
|
96
|
+
super
|
97
|
+
ensure
|
98
|
+
(Table.list << other).uniq!
|
99
|
+
basename = other.name.split(%r/::/).last.underscore
|
100
|
+
Table.singleton_class{ attribute basename => other }
|
101
|
+
other.send :extend, RecursivelyInherited
|
102
|
+
end
|
103
|
+
end
|
104
|
+
send :extend, RecursivelyInherited
|
105
|
+
|
106
|
+
#
|
107
|
+
# table classes
|
108
|
+
#
|
109
|
+
class Job < Table
|
110
|
+
set_table_name "bj_job"
|
111
|
+
set_primary_key "#{ table_name }_id"
|
112
|
+
|
113
|
+
migration {
|
114
|
+
define_method :up do
|
115
|
+
create_table table.table_name, :primary_key => table.primary_key, :force => true do |t|
|
116
|
+
t.column "command" , :text
|
117
|
+
|
118
|
+
t.column "state" , :text
|
119
|
+
t.column "priority" , :integer
|
120
|
+
t.column "tag" , :text
|
121
|
+
|
122
|
+
t.column "submitter" , :text
|
123
|
+
t.column "runner" , :text
|
124
|
+
t.column "pid" , :integer
|
125
|
+
|
126
|
+
t.column "submitted_at" , :datetime
|
127
|
+
t.column "started_at" , :datetime
|
128
|
+
t.column "finished_at" , :datetime
|
129
|
+
|
130
|
+
t.column "env" , :text
|
131
|
+
t.column "stdin" , :text
|
132
|
+
t.column "stdout" , :text
|
133
|
+
t.column "stderr" , :text
|
134
|
+
t.column "exit_status" , :integer
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
define_method :down do
|
139
|
+
drop_table table.table_name
|
140
|
+
end
|
141
|
+
}
|
142
|
+
|
143
|
+
module ClassMethods
|
144
|
+
def submit jobs, options = {}, &block
|
145
|
+
jobs = Joblist.for jobs, options
|
146
|
+
returned = []
|
147
|
+
transaction do
|
148
|
+
jobs.each do |job|
|
149
|
+
job = create_hash_for(job.reverse_merge(submit_defaults))
|
150
|
+
job = create! job
|
151
|
+
returned << (block ? block.call(job) : job)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
returned
|
155
|
+
end
|
156
|
+
|
157
|
+
def submit_defaults
|
158
|
+
{
|
159
|
+
:state => "pending",
|
160
|
+
:priority => 0,
|
161
|
+
:tag => "",
|
162
|
+
:submitter => Bj.hostname,
|
163
|
+
:submitted_at => Time.now,
|
164
|
+
}
|
165
|
+
end
|
166
|
+
end
|
167
|
+
send :extend, ClassMethods
|
168
|
+
|
169
|
+
module InstanceMethods
|
170
|
+
def finished
|
171
|
+
reload
|
172
|
+
exit_status
|
173
|
+
end
|
174
|
+
alias_method "finished?", "finished"
|
175
|
+
end
|
176
|
+
send :include, InstanceMethods
|
177
|
+
end
|
178
|
+
|
179
|
+
class JobArchive < Job
|
180
|
+
set_table_name "bj_job_archive"
|
181
|
+
set_primary_key "#{ table_name }_id"
|
182
|
+
|
183
|
+
migration {
|
184
|
+
define_method(:up) do
|
185
|
+
create_table table.table_name, :primary_key => table.primary_key, :force => true do |t|
|
186
|
+
t.column "command" , :text
|
187
|
+
|
188
|
+
t.column "state" , :text
|
189
|
+
t.column "priority" , :integer
|
190
|
+
t.column "tag" , :text
|
191
|
+
|
192
|
+
t.column "submitter" , :text
|
193
|
+
t.column "runner" , :text
|
194
|
+
t.column "pid" , :integer
|
195
|
+
|
196
|
+
t.column "submitted_at" , :datetime
|
197
|
+
t.column "started_at" , :datetime
|
198
|
+
t.column "finished_at" , :datetime
|
199
|
+
t.column "archived_at" , :datetime
|
200
|
+
|
201
|
+
t.column "env" , :text
|
202
|
+
t.column "stdin" , :text
|
203
|
+
t.column "stdout" , :text
|
204
|
+
t.column "stderr" , :text
|
205
|
+
t.column "exit_status" , :integer
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
define_method(:down) do
|
210
|
+
drop_table table.table_name
|
211
|
+
end
|
212
|
+
}
|
213
|
+
end
|
214
|
+
|
215
|
+
# TODO - initialize with a set of global defaults and fallback to those on perhaps '* * key'
|
216
|
+
class Config < Table
|
217
|
+
set_table_name "bj_config"
|
218
|
+
set_primary_key "#{ table_name }_id"
|
219
|
+
|
220
|
+
migration {
|
221
|
+
define_method(:up) do
|
222
|
+
create_table table.table_name, :primary_key => table.primary_key, :force => true do |t|
|
223
|
+
t.column "hostname" , :text
|
224
|
+
t.column "key" , :text
|
225
|
+
t.column "value" , :text
|
226
|
+
t.column "cast" , :text
|
227
|
+
end
|
228
|
+
|
229
|
+
add_index table.table_name, %w[ hostname key ], :unique => true
|
230
|
+
end
|
231
|
+
|
232
|
+
define_method(:down) do
|
233
|
+
remove_index table.table_name, :column => %w[ hostname key ]
|
234
|
+
drop_table table.table_name
|
235
|
+
end
|
236
|
+
}
|
237
|
+
|
238
|
+
module ClassMethods
|
239
|
+
def [] key
|
240
|
+
get key
|
241
|
+
end
|
242
|
+
|
243
|
+
def get key, options = {}
|
244
|
+
transaction do
|
245
|
+
options.to_options!
|
246
|
+
hostname = options[:hostname] || Bj.hostname
|
247
|
+
record = find :first, :conditions => conditions(:key => key, :hostname => hostname)
|
248
|
+
record ? record.value : default_for(key)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def conditions options = {}
|
253
|
+
options.to_options!
|
254
|
+
options.reverse_merge!(
|
255
|
+
:hostname => Bj.hostname
|
256
|
+
)
|
257
|
+
options
|
258
|
+
end
|
259
|
+
|
260
|
+
def default_for key
|
261
|
+
record = find :first, :conditions => conditions(:key => key, :hostname => '*')
|
262
|
+
record ? record.value : nil
|
263
|
+
end
|
264
|
+
|
265
|
+
def []= key, value
|
266
|
+
set key, value
|
267
|
+
end
|
268
|
+
|
269
|
+
def set key, value, options = {}
|
270
|
+
transaction do
|
271
|
+
options.to_options!
|
272
|
+
hostname = options[:hostname] || Bj.hostname
|
273
|
+
record = find :first, :conditions => conditions(:key => key, :hostname => hostname)
|
274
|
+
cast = options[:cast] || cast_for(value)
|
275
|
+
key = key.to_s
|
276
|
+
value = value.to_s
|
277
|
+
if record
|
278
|
+
record["value"] = value
|
279
|
+
record["cast"] = cast
|
280
|
+
record.save!
|
281
|
+
else
|
282
|
+
create! :hostname => hostname, :key => key, :value => value, :cast => cast
|
283
|
+
end
|
284
|
+
value
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
def delete key
|
289
|
+
transaction do
|
290
|
+
record = find :first, :conditions => conditions(:key => key)
|
291
|
+
if record
|
292
|
+
record.destroy
|
293
|
+
record
|
294
|
+
else
|
295
|
+
nil
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def has_key? key
|
301
|
+
record = find :first, :conditions => conditions(:key => key)
|
302
|
+
record ? record : false
|
303
|
+
end
|
304
|
+
alias_method "has_key", "has_key?"
|
305
|
+
|
306
|
+
def keys
|
307
|
+
find(:all, :conditions => conditions).map(&:key)
|
308
|
+
end
|
309
|
+
|
310
|
+
def values
|
311
|
+
find(:all, :conditions => conditions).map(&:value)
|
312
|
+
end
|
313
|
+
|
314
|
+
def for options = {}
|
315
|
+
oh = OrderedHash.new
|
316
|
+
find(:all, :conditions => conditions(options)).each do |record|
|
317
|
+
oh[record.key] = record.value
|
318
|
+
end
|
319
|
+
oh
|
320
|
+
end
|
321
|
+
|
322
|
+
def cast_for value
|
323
|
+
case value
|
324
|
+
when TrueClass, FalseClass
|
325
|
+
'to_bool'
|
326
|
+
when NilClass
|
327
|
+
'to_nil'
|
328
|
+
when Fixnum, Bignum
|
329
|
+
'to_i'
|
330
|
+
when Float
|
331
|
+
'to_f'
|
332
|
+
when Time
|
333
|
+
'to_time'
|
334
|
+
when Symbol
|
335
|
+
'to_sym'
|
336
|
+
else
|
337
|
+
case value.to_s
|
338
|
+
when %r/^\d+$/
|
339
|
+
'to_i'
|
340
|
+
when %r/^\d+\.\d+$/
|
341
|
+
'to_f'
|
342
|
+
when %r/^nil$|^$/
|
343
|
+
'to_nil'
|
344
|
+
when %r/^true|false$/
|
345
|
+
'to_bool'
|
346
|
+
else
|
347
|
+
'to_s'
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
def casts
|
353
|
+
@casts ||= {
|
354
|
+
'to_bool' => lambda do |value|
|
355
|
+
value.to_s =~ %r/^true$/i ? true : false
|
356
|
+
end,
|
357
|
+
'to_i' => lambda do |value|
|
358
|
+
Integer value.to_s.gsub(%r/^(-)?0*/,'\1')
|
359
|
+
end,
|
360
|
+
'to_f' => lambda do |value|
|
361
|
+
Float value.to_s.gsub(%r/^0*/,'')
|
362
|
+
end,
|
363
|
+
'to_time' => lambda do |value|
|
364
|
+
Time.parse(value.to_s)
|
365
|
+
end,
|
366
|
+
'to_sym' => lambda do |value|
|
367
|
+
value.to_s.to_sym
|
368
|
+
end,
|
369
|
+
'to_nil' => lambda do |value|
|
370
|
+
value.to_s =~ %r/^nil$|^$/i ? nil : value.to_s
|
371
|
+
end,
|
372
|
+
'to_s' => lambda do |value|
|
373
|
+
value.to_s
|
374
|
+
end,
|
375
|
+
}
|
376
|
+
end
|
377
|
+
end
|
378
|
+
send :extend, ClassMethods
|
379
|
+
|
380
|
+
module InstanceMethods
|
381
|
+
def value
|
382
|
+
self.class.casts[cast][self["value"]]
|
383
|
+
end
|
384
|
+
end
|
385
|
+
send :include, InstanceMethods
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|