rbbt-util 3.0.2 → 3.0.3

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/lib/rbbt/util/log.rb CHANGED
@@ -16,13 +16,15 @@ module Log
16
16
  @@severity
17
17
  end
18
18
 
19
- SEVERITY_COLOR = ["0;37m", "32m", "33m", "31m", "1;0m" ].collect{|e| "\033[#{e}"}
19
+ SEVERITY_COLOR = ["0;37m", "0;32m", "0;33m", "0;31m", "1;0m" ].collect{|e| "\033[#{e}"}
20
20
 
21
21
  def self.log(message, severity = MEDIUM)
22
22
  severity_color = SEVERITY_COLOR[severity]
23
+ font_color = {false => "\033[0;37m", true => "\033[0m"}[severity >= INFO]
24
+
23
25
  STDERR.puts caller.select{|l| l =~ /rbbt/} * "\n" if @@severity == -1 and not message.empty?
24
26
  #STDERR.puts "#{Time.now.strftime("[%m/%d/%y-%H:%M:%S]")}[#{severity.to_s}]: " + message if severity >= @@severity
25
- STDERR.puts "\033[0;37m#{Time.now.strftime("[%m/%d/%y-%H:%M:%S]")}#{severity_color}[#{severity.to_s}]\033[0m: " + message if severity >= @@severity
27
+ STDERR.puts "\033[0;37m#{Time.now.strftime("[%m/%d/%y-%H:%M:%S]")}#{severity_color}[#{severity.to_s}]\033[0m:#{font_color} " << message.strip << "\033[0m" if severity >= @@severity
26
28
  end
27
29
 
28
30
  def self.debug(message)
@@ -41,6 +43,10 @@ module Log
41
43
  log(message, HIGH)
42
44
  end
43
45
 
46
+ def self.info(message)
47
+ log(message, INFO)
48
+ end
49
+
44
50
  def self.warn(message)
45
51
  log(message, WARN)
46
52
  end
@@ -1,4 +1,5 @@
1
1
  require 'iconv'
2
+ require 'lockfile'
2
3
  require 'digest/md5'
3
4
 
4
5
  class RBBTError < StandardError
@@ -14,9 +15,32 @@ class RBBTError < StandardError
14
15
  end
15
16
  end
16
17
 
18
+ module IndiferentHash
19
+ def indiferent_get(key)
20
+ old_get(key) ||
21
+ old_get(key.to_s) ||
22
+ old_get(key.to_sym)
23
+ end
24
+
25
+ def self.extended(base)
26
+ class << base
27
+ alias_method :old_get, :[]
28
+ alias_method :[], :indiferent_get
29
+ end
30
+ end
31
+ end
32
+
17
33
  module Misc
18
34
  class FieldNotFoundError < StandardError;end
19
35
 
36
+ def self.lock(file, *args)
37
+ FileUtils.mkdir_p File.dirname(File.expand_path(file)) unless File.exists? File.dirname(File.expand_path(file))
38
+ lockfile = Lockfile.new file + '.lock'
39
+ lockfile.lock do
40
+ yield file, *args
41
+ end
42
+ end
43
+
20
44
  def self.string2const(string)
21
45
  return nil if string.nil?
22
46
  mod = Kernel
@@ -137,15 +161,19 @@ module Misc
137
161
 
138
162
  def self.hash2md5(hash)
139
163
  o = {}
140
- hash.each do |k,v|
141
- if v.inspect =~ /:0x0/
164
+ hash.keys.sort_by{|k| k.to_s}.each do |k|
165
+ v = hash[k]
166
+ case
167
+ when v.inspect =~ /:0x0/
142
168
  o[k] = v.inspect.sub(/:0x[a-f0-9]+@/,'')
169
+ when Resource::Path === v
170
+ "" << String.new(v.to_s)
143
171
  else
144
172
  o[k] = v
145
173
  end
146
174
  end
147
175
 
148
- Digest::MD5.hexdigest(o.inspect)
176
+ Digest::MD5.hexdigest(o.sort_by{|k| k.to_s}.inspect)
149
177
  end
150
178
 
151
179
  def self.string2hash(string)
@@ -200,9 +200,21 @@ module Open
200
200
  end
201
201
  end
202
202
 
203
- def self.write(file, content)
203
+ def self.write(file, content = nil)
204
204
  FileUtils.mkdir_p File.dirname(file)
205
- if String === content
205
+ case
206
+ when content.nil?
207
+ begin
208
+ File.open(file, 'w') do |f|
209
+ f.flock(File::LOCK_EX)
210
+ yield f
211
+ f.flock(File::LOCK_UN)
212
+ end
213
+ rescue Exception
214
+ FileUtils.rm file if File.exists? file
215
+ raise $!
216
+ end
217
+ when String === content
206
218
  File.open(file, 'w') do |f|
207
219
  f.flock(File::LOCK_EX)
208
220
  f.write content
@@ -217,8 +229,9 @@ module Open
217
229
  end
218
230
  f.flock(File::LOCK_UN)
219
231
  end
220
- rescue
232
+ rescue Exception
221
233
  FileUtils.rm file if File.exists? file
234
+ raise $!
222
235
  end
223
236
  content.close
224
237
  end
@@ -181,6 +181,7 @@ module Persistence
181
181
  FileUtils.rm persistence_file
182
182
  end
183
183
 
184
+ Log.debug "Dump data into '#{persistence_file}'"
184
185
  per = Persistence::TSV.get persistence_file, true, serializer
185
186
 
186
187
  per.write
@@ -227,30 +228,41 @@ module Persistence
227
228
 
228
229
  serializer = tsv_serializer res, extra
229
230
 
230
- per = Persistence::TSV.get persistence_file, true, serializer
231
+ begin
232
+ per = Persistence::TSV.get persistence_file, true, serializer
231
233
 
232
- per.write
233
- per.merge! res
234
- Persistence::TSV::FIELD_INFO_ENTRIES.keys.each do |key|
235
- if extra.include?(key.to_sym) and per.respond_to?(key.to_sym)
236
- per.send "#{key}=".to_sym, extra[key.to_sym]
234
+ per.write
235
+ per.merge! res
236
+ Persistence::TSV::FIELD_INFO_ENTRIES.keys.each do |key|
237
+ if extra.include?(key.to_sym) and per.respond_to?(key.to_sym)
238
+ per.send "#{key}=".to_sym, extra[key.to_sym]
239
+ end
237
240
  end
241
+ rescue Exception
242
+ per.close
243
+ raise $!
238
244
  end
245
+
239
246
  per.read
240
247
 
241
248
  [ per, extra ]
242
249
  else
243
250
  Log.debug "Loading #{ persistence_file }. Prefix = #{prefix}"
244
- per = Persistence::TSV.get persistence_file, true, serializer
245
-
246
- extra = {}
247
- Persistence::TSV::FIELD_INFO_ENTRIES.keys.each do |key|
248
- if per.respond_to?(key.to_sym)
249
- extra[key] = per.send(key.to_sym)
251
+ begin
252
+ per = Persistence::TSV.get persistence_file, true, serializer
253
+
254
+ extra = {}
255
+ Persistence::TSV::FIELD_INFO_ENTRIES.keys.each do |key|
256
+ if per.respond_to?(key.to_sym)
257
+ extra[key] = per.send(key.to_sym)
258
+ end
250
259
  end
251
- end
252
260
 
253
- [ per, extra ]
261
+ rescue Exception
262
+ per.close
263
+ raise $!
264
+ end
265
+ [ per, extra ]
254
266
  end
255
267
  end
256
268
 
@@ -314,16 +326,16 @@ module Persistence
314
326
 
315
327
  filename = get_filename(file)
316
328
 
317
- if persistence == :no_create
318
- o = options.dup
319
- options =
320
- Misc.add_defaults options, :persistence_update => false, :persistence_file => nil, :filename => nil
321
- persistence_update, persistence_dir, persistence_file, filename =
322
- Misc.process_options options, :persistence_update, :persistence_dir, :persistence_file, :filename
329
+ o = options.dup
330
+ o =
331
+ Misc.add_defaults o, :persistence_update => false, :persistence_file => nil, :filename => nil
332
+ persistence_update, persistence_dir, persistence_file, filename =
333
+ Misc.process_options o, :persistence_update, :persistence_dir, :persistence_file, :filename
323
334
 
324
- filename ||= get_filename(file)
325
- persistence_file ||= get_persistence_file(filename, prefix, options.merge(:persistence_dir => persistence_dir))
335
+ filename ||= get_filename(file)
336
+ persistence_file ||= get_persistence_file(filename, prefix, o.merge(:persistence_dir => persistence_dir))
326
337
 
338
+ if persistence == :no_create
327
339
  persistence = false if not File.exists? persistence_file
328
340
  end
329
341
 
@@ -333,21 +345,24 @@ module Persistence
333
345
  else
334
346
  Log.low "Persistent Loading for #{filename}. Prefix: #{prefix}. Type #{persistence_type.to_s}"
335
347
 
336
- case persistence_type.to_sym
337
- when :string
338
- persist_string(file, prefix, options, &block)
339
- when :marshal
340
- persist_marshal(file, prefix, options, &block)
341
- when :yaml
342
- persist_yaml(file, prefix, options, &block)
343
- when :tsv
344
- persist_tsv(file, prefix, options, &block)
345
- when :tsv_string
346
- persist_tsv_string(file, prefix, options, &block)
347
- when :tsv_extra
348
- persist_tsv_extra(file, prefix, options, &block)
349
- when :fwt
350
- persist_fwt(file, prefix, options, &block)
348
+ Misc.lock(persistence_file, file, prefix, options, block) do |persistence_file,file,prefix,options,block|
349
+
350
+ case persistence_type.to_sym
351
+ when :string
352
+ persist_string(file, prefix, options, &block)
353
+ when :marshal
354
+ persist_marshal(file, prefix, options, &block)
355
+ when :yaml
356
+ persist_yaml(file, prefix, options, &block)
357
+ when :tsv
358
+ persist_tsv(file, prefix, options, &block)
359
+ when :tsv_string
360
+ persist_tsv_string(file, prefix, options, &block)
361
+ when :tsv_extra
362
+ persist_tsv_extra(file, prefix, options, &block)
363
+ when :fwt
364
+ persist_fwt(file, prefix, options, &block)
365
+ end
351
366
  end
352
367
  end
353
368
  end
@@ -240,6 +240,14 @@ source "$INSTALL_HELPER_FILE"
240
240
  Open.read self.find, *args
241
241
  end
242
242
 
243
+ def yaml(*args)
244
+ YAML.load(open)
245
+ end
246
+
247
+ def marshal(*args)
248
+ Marshal.load(open)
249
+ end
250
+
243
251
  def write(content, *args)
244
252
  FileUtils.mkdir_p File.dirname(self.find) unless File.exists? self.find
245
253
  Open.write(self.find, content, *args)
@@ -1,4 +1,5 @@
1
1
  require 'rbbt/util/open'
2
+ require 'rbbt/util/task/job'
2
3
 
3
4
  class Task
4
5
  class << self
@@ -7,246 +8,12 @@ class Task
7
8
 
8
9
  @basedir = "."
9
10
 
10
- class Job
11
- attr_accessor :task, :id, :name, :options, :dependencies, :pid, :path, :previous_jobs
12
-
13
- IDSEP = "_"
14
-
15
- def self.id2name(job_id)
16
- job_id.split(IDSEP)
17
- end
18
-
19
- def self.load(task, id)
20
- name, hash = id2name(id)
21
- self.new task, id, name, nil, nil
22
- end
23
-
24
- def initialize(task, id, name, options, dependencies)
25
- @task = task
26
- @id =id
27
- @name = name
28
- @options = options
29
- @dependencies = dependencies
30
-
31
- if @dependencies
32
- @previous_jobs = Hash[*dependencies.first.collect{|job| job.task.name}.zip(dependencies.first).flatten]
33
- dependencies.first.collect{|job| @previous_jobs.merge! job.previous_jobs }
34
- else
35
- @previous_jobs = []
36
- end
37
-
38
- basedir = task.workflow.jobdir unless task.workflow.nil?
39
- @path = File.join(basedir || Task.basedir, task.name, id)
40
- end
41
-
42
- def info_file
43
- path + '.info'
44
- end
45
-
46
- def info
47
- return {} if not File.exists?(info_file)
48
- YAML.load(File.open(info_file))
49
- end
50
-
51
- def set_info(key, value)
52
- i = self.info
53
- new_info = i.merge(key => value)
54
- Open.write(info_file, new_info.to_yaml)
55
- end
56
-
57
- def step(name = nil, message = nil)
58
- if name.nil?
59
- info[:step]
60
- else
61
- set_info(:step, name)
62
- set_info(:messages, info[:messages] || [] << message) if not message.nil?
63
- end
64
- end
65
-
66
- def messages
67
- info[:messages] || []
68
- end
69
-
70
- def done?
71
- [:done, :error, :aborted].include? info[:step]
72
- end
73
-
74
- def error?
75
- step == :error or step == :aborted
76
- end
77
-
78
- def arguments
79
- options.values_at *task.options
80
- end
81
-
82
- def block
83
- task.block
84
- end
85
-
86
- def run_dependencies
87
- jobs, files = dependencies
88
- jobs.each do |job| job.start unless File.exists? job.path; job.step(:done) end
89
- files.each do |file| file.produce end
90
- end
91
-
92
- def input
93
- dep = dependencies.flatten.first
94
- if Job === dep
95
- dep.load
96
- else
97
- dep.read
98
- end
99
- end
100
-
101
- def start
102
- Log.medium("Starting Job '#{ name }'. Path: '#{ path }'")
103
- set_info(:start_time, Time.now)
104
- save_options(options)
105
-
106
- extend task.scope unless task.scope.nil? or Object == task.scope.class
107
-
108
- begin
109
-
110
- if dependencies.flatten.any?
111
- run_dependencies
112
- end
113
-
114
- result = instance_exec *arguments, &block
115
-
116
-
117
- if not result.nil?
118
- case task.persistence
119
- when nil, :string, :tsv, :integer
120
- Open.write(path, result.to_s)
121
- when :marshal
122
- Open.write(path, Marshal.dump(result))
123
- when :yaml
124
- Open.write(path, YAML.dump(result))
125
- end
126
- end
127
-
128
- set_info(:end_time, Time.now)
129
- Log.medium("Finished Job '#{ name }'. Path: '#{ path }'")
130
- rescue Exception
131
- step(:error, "#{$!.class}: #{$!.message}")
132
- raise $!
133
- end
134
- end
135
-
136
- def save_options(options)
137
- new_options = {}
138
- options.each do |key, value|
139
- case
140
- when TSV === value
141
- new_options[key] = value.to_s
142
- else
143
- new_options[key] = value
144
- end
145
- end
146
- set_info(:options, new_options)
147
- end
148
-
149
- def recursive_done?
150
- previous_jobs.values.inject(true){|acc,j| acc and j.recursive_done?} and done?
151
- end
152
-
153
- def run
154
- return self if recursive_done?
155
- begin
156
- step(:started)
157
- start
158
- step(:done)
159
- rescue Exception
160
- Log.debug $!.message
161
- Log.debug $!.backtrace * "\n"
162
- step(:error, "#{$!.class}: #{$!.message}")
163
- end
164
- self
165
- end
166
-
167
- def fork
168
- return self if recursive_done?
169
- @pid = Process.fork do
170
- begin
171
- step(:started)
172
- start
173
- step(:done)
174
- rescue Exception
175
- Log.debug $!.message
176
- Log.debug $!.backtrace * "\n"
177
- step(:error, "#{$!.class}: #{$!.message}")
178
- end
179
- exit
180
- end
181
-
182
- self
183
- end
184
-
185
- def join
186
- if @pid.nil?
187
- while not done? do
188
- Log.debug "Waiting: #{info[:step]}"
189
- sleep 5
190
- end
191
- else
192
- Process.waitpid @pid
193
- end
194
-
195
- self
196
- end
197
-
198
- def open
199
- File.open(path)
200
- end
201
-
202
- def read
203
- File.open(path) do |f| f.read end
204
- end
205
-
206
- def load
207
- case task.persistence
208
- when :float
209
- Open.read(path).to_f
210
- when :integer
211
- Open.read(path).to_i
212
- when :string
213
- Open.read(path)
214
- when :tsv
215
- TSV.new(path)
216
- when :marshal
217
- Marshal.load(Open.read(path))
218
- when :yaml
219
- YAML.load(Open.read(path))
220
- end
221
- end
222
-
223
- def clean
224
- FileUtils.rm path if File.exists? path
225
- FileUtils.rm info_file if File.exists? info_file
226
- end
227
-
228
- def recursive_clean
229
- dependencies.first.each do |job| job.recursive_clean end
230
- clean
231
- end
232
- end # END Job
233
-
234
11
  def load(job_id)
235
12
  Job.load(self, job_id)
236
13
  end
237
14
 
238
- def job_options(run_options = nil)
239
- return {} if options.nil?
240
-
241
- job_options = {}
242
- options.each do |option|
243
- job_options[option] = Misc.process_options run_options, option
244
- end
245
-
246
- job_options
247
- end
248
-
249
- def job_id(name, job_options)
15
+ def job_id(name, job_options, previous_jobs)
16
+ job_options = job_options.merge :previous_jobs => previous_jobs.collect{|job| job.id} if previous_jobs.any?
250
17
  if job_options.any?
251
18
  name.to_s + "_" + Misc.hash2md5(job_options)
252
19
  else
@@ -254,8 +21,8 @@ class Task
254
21
  end
255
22
  end
256
23
 
257
- attr_accessor :name, :persistence, :options, :option_descriptions, :option_types, :option_defaults, :workflow, :dependencies, :scope, :block
258
- def initialize(name, persistence = nil, options = nil, option_descriptions = nil, option_types = nil, option_defaults = nil, workflow = nil, dependencies = nil, scope = nil, &block)
24
+ attr_accessor :name, :persistence, :options, :option_descriptions, :option_types, :option_defaults, :workflow, :dependencies, :scope, :description, :block
25
+ def initialize(name, persistence = nil, options = nil, option_descriptions = nil, option_types = nil, option_defaults = nil, workflow = nil, dependencies = nil, scope = nil, description = nil, &block)
259
26
  dependencies = [dependencies] unless dependencies.nil? or Array === dependencies
260
27
  @name = name.to_s
261
28
 
@@ -269,68 +36,61 @@ class Task
269
36
  @workflow = workflow
270
37
  @dependencies = dependencies || []
271
38
  @scope = scope
39
+ @description = description
272
40
 
273
41
  @block = block unless not block_given?
274
42
  end
275
43
 
276
- def recursive_options
277
- all_options = []
278
- option_descriptions = {}
279
- option_types = {}
280
- option_defaults = {}
281
-
282
- all_options.concat self.options if self.options
283
- option_descriptions.merge! self.option_descriptions if self.option_descriptions
284
- option_types.merge! self.option_types if self.option_types
285
- option_defaults.merge! self.option_defaults if self.option_defaults
44
+ def process_options(args, optional_args)
45
+ run_options = {}
286
46
 
287
- self.dependencies.each do |task|
288
- task = case
289
- when Symbol === task
290
- workflow.tasks[task]
291
- when Task === task
292
- task
293
- else
294
- next
295
- end
296
-
297
- n_all_options, n_option_descriptions, n_option_types, n_option_defaults = task.recursive_options
298
-
299
- all_options.concat n_all_options if n_all_options
300
- option_descriptions.merge! n_option_descriptions if n_option_descriptions
301
- option_types.merge! n_option_types if n_option_types
302
- option_defaults.merge! n_option_defaults if n_option_defaults
303
- end
304
-
305
- [all_options, option_descriptions, option_types, option_defaults]
47
+ options.each do |option|
48
+ if option_defaults and option_defaults.include? option
49
+ run_options[option] = Misc.process_options(optional_args, option) || option_defaults[option]
50
+ else
51
+ run_options[option] = args.shift
52
+ end
53
+ end unless options.nil?
54
+
55
+ [run_options, args, optional_args]
306
56
  end
307
57
 
308
- def job_dependencies(jobname, run_options = {})
309
- jobs = []
310
- files = []
58
+ def setup(jobname, args, optional_args, dependencies)
59
+ previous_jobs = []
60
+ required_files = []
61
+
62
+ run_options, args, optional_args = process_options args, optional_args
63
+
311
64
  dependencies.each do |dependency|
312
65
  case
66
+ when Proc === dependency
67
+ previous_jobs << dependency.call(jobname, run_options)
313
68
  when Task === dependency
314
- jobs << dependency.job(jobname, run_options)
69
+ previous_jobs << dependency.job(jobname, *(args + [optional_args]))
70
+ when Task::Job === dependency
71
+ previous_jobs << dependency
315
72
  when Symbol === dependency
316
- raise "No workflow defined, yet dependencies include Symbols (other tasks)" if workflow.nil?
317
- jobs << workflow.tasks[dependency].job(jobname, run_options)
73
+ previous_jobs << workflow.tasks[dependency].job(jobname, *(args + [optional_args]))
318
74
  else
319
- files << dependency
75
+ required_files << dependency
320
76
  end
321
77
  end
322
- [jobs, files]
323
- end
324
78
 
325
- def job(jobname, run_options = {})
79
+ [previous_jobs, required_files, run_options]
80
+ end
326
81
 
327
- job_id = self.job_id jobname, run_options
82
+ def job(jobname, *args)
83
+ if Hash === args.last
84
+ optional_args = args.pop
85
+ else
86
+ optional_args = {}
87
+ end
328
88
 
329
- job_options = self.job_options run_options
89
+ previous_jobs, required_files, run_options = setup(jobname, args, optional_args, dependencies)
330
90
 
331
- dependencies = self.job_dependencies(jobname, run_options)
91
+ job_id = self.job_id jobname, run_options, previous_jobs
332
92
 
333
- Job.new(self, job_id, jobname, job_options, dependencies)
93
+ Job.new(self, job_id, jobname, run_options, previous_jobs, required_files, previous_jobs.first)
334
94
  end
335
95
 
336
96
  def run(*args)