scout-gear 10.8.3 → 10.9.0

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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.vimproject +17 -0
  3. data/README.md +352 -0
  4. data/Rakefile +1 -0
  5. data/VERSION +1 -1
  6. data/doc/Association.md +288 -0
  7. data/doc/Entity.md +296 -0
  8. data/doc/KnowledgeBase.md +433 -0
  9. data/doc/Persist.md +356 -0
  10. data/doc/Semaphore.md +171 -0
  11. data/doc/TSV.md +449 -0
  12. data/doc/WorkQueue.md +359 -0
  13. data/doc/Workflow.md +586 -0
  14. data/lib/scout/association.rb +4 -2
  15. data/lib/scout/entity/identifiers.rb +1 -1
  16. data/lib/scout/entity/object.rb +1 -1
  17. data/lib/scout/entity/property.rb +5 -5
  18. data/lib/scout/entity.rb +1 -1
  19. data/lib/scout/knowledge_base/description.rb +1 -1
  20. data/lib/scout/knowledge_base/list.rb +7 -2
  21. data/lib/scout/knowledge_base/registry.rb +2 -2
  22. data/lib/scout/knowledge_base.rb +20 -2
  23. data/lib/scout/monitor.rb +300 -0
  24. data/lib/scout/persist/engine/packed_index.rb +2 -2
  25. data/lib/scout/persist/engine/sharder.rb +1 -1
  26. data/lib/scout/persist/tsv.rb +1 -0
  27. data/lib/scout/semaphore.rb +1 -1
  28. data/lib/scout/tsv/dumper.rb +3 -3
  29. data/lib/scout/tsv/open.rb +1 -0
  30. data/lib/scout/tsv/parser.rb +1 -1
  31. data/lib/scout/tsv/transformer.rb +1 -0
  32. data/lib/scout/tsv/util.rb +2 -2
  33. data/lib/scout/work_queue/socket.rb +1 -1
  34. data/lib/scout/work_queue/worker.rb +7 -5
  35. data/lib/scout/workflow/documentation.rb +1 -1
  36. data/lib/scout/workflow/entity.rb +22 -1
  37. data/lib/scout/workflow/step/config.rb +3 -3
  38. data/lib/scout/workflow/step/file.rb +4 -0
  39. data/lib/scout/workflow/step/info.rb +8 -2
  40. data/lib/scout/workflow/step.rb +10 -5
  41. data/lib/scout/workflow/task/inputs.rb +1 -1
  42. data/lib/scout/workflow/usage.rb +3 -2
  43. data/lib/scout/workflow/util.rb +22 -0
  44. data/scout-gear.gemspec +20 -6
  45. data/scout_commands/cat +86 -0
  46. data/scout_commands/doc +3 -1
  47. data/scout_commands/entity +151 -0
  48. data/scout_commands/system/clean +146 -0
  49. data/scout_commands/system/status +238 -0
  50. data/scout_commands/workflow/info +23 -10
  51. data/scout_commands/workflow/install +1 -1
  52. data/scout_commands/workflow/task +1 -1
  53. data/test/scout/entity/test_property.rb +1 -1
  54. data/test/scout/knowledge_base/test_registry.rb +19 -0
  55. data/test/scout/test_work_queue.rb +1 -1
  56. data/test/scout/work_queue/test_worker.rb +12 -10
  57. metadata +32 -5
  58. data/doc/lib/scout/path.md +0 -35
  59. data/doc/lib/scout/workflow/task.md +0 -13
@@ -12,7 +12,7 @@ class KnowledgeBase
12
12
  if entity_type.to_s == "simple"
13
13
  path = dir.lists[entity_type.to_s][id]
14
14
  else
15
- path = dir.lists[entity_type.to_s][id + ".tsv"]
15
+ path = dir.lists[entity_type.to_s][id].find_with_extension("tsv")
16
16
  end
17
17
  else
18
18
  path = dir.lists.glob("*/#{id}").first
@@ -33,6 +33,7 @@ class KnowledgeBase
33
33
  Open.lock path do
34
34
  begin
35
35
  if AnnotatedArray === list
36
+ path = path.set_extension('tsv')
36
37
  Open.write(path, Annotation.tsv(list, :all).to_s)
37
38
  else
38
39
  Open.write(path, list * "\n")
@@ -60,7 +61,11 @@ class KnowledgeBase
60
61
  list.extend AnnotatedArray
61
62
  list
62
63
  else
63
- path.list
64
+ list = path.list
65
+ if entity_type
66
+ Entity.prepare_entity(list, entity_type)
67
+ end
68
+ list
64
69
  end
65
70
  rescue
66
71
  Log.exception $!
@@ -84,7 +84,7 @@ class KnowledgeBase
84
84
  key = name.to_s + "_" + fp
85
85
  end
86
86
 
87
- Persist.memory("Index:" << [key, dir] * "@") do
87
+ Persist.memory("Index:" + [key, dir] * "@") do
88
88
  options = options.dup
89
89
 
90
90
  persist_dir = dir
@@ -148,7 +148,7 @@ class KnowledgeBase
148
148
  options[:namespace] ||= self.namespace unless self.namespace.nil?
149
149
 
150
150
  key += '.database'
151
- Persist.memory("Database:" << [key, dir] * "@") do
151
+ Persist.memory("Database:" + [key, dir] * "@") do
152
152
  options = options.dup
153
153
 
154
154
  persist_dir = dir
@@ -59,8 +59,26 @@ class KnowledgeBase
59
59
  end
60
60
 
61
61
  def self.load(dir)
62
- dir = Path.setup("var").knowledge_base[dir.to_s] if Symbol === dir
63
- kb = KnowledgeBase.new dir
62
+ kb = case dir
63
+ when Path
64
+ KnowledgeBase.new dir
65
+ when Symbol
66
+ dir = Path.setup("var").knowledge_base[dir.to_s] if Symbol === dir
67
+ kb = KnowledgeBase.new dir
68
+ when Workflow
69
+ raise if dir.knowledge_base.nil?
70
+ kb = dir.knowledge_base
71
+ when String
72
+ if Workflow.list.include? dir
73
+ workflow = Workflow.require_workflow dir
74
+ kb = workflow.knowledge_base
75
+ elsif dir =~ /^\w+$/
76
+ load(dir.to_sym)
77
+ else
78
+ kb = KnowledgeBase.new dir
79
+ end
80
+ end
81
+
64
82
  kb.load
65
83
  kb
66
84
  end
@@ -0,0 +1,300 @@
1
+ require 'scout'
2
+
3
+ module Scout
4
+
5
+ SENSIBLE_WRITE_DIRS = Open.sensible_write_dir.find_all
6
+
7
+ LOCK_DIRS = Path.setup('tmp/tsv_open_locks').find_all +
8
+ Path.setup('tmp/tsv_locks').find_all +
9
+ Path.setup('tmp/persist_locks').find_all +
10
+ Path.setup('tmp/sensible_write_locks').find_all +
11
+ Path.setup('tmp/produce_locks').find_all +
12
+ Path.setup('tmp/step_info_locks').find_all
13
+
14
+ PERSIST_DIRS = Path.setup('share').find_all + Path.setup('var/cache/persistence').find_all
15
+
16
+ JOB_DIRS = Path.setup('var/jobs').find_all
17
+
18
+ MUTEX_FOR_THREAD_EXCLUSIVE = Mutex.new
19
+
20
+ def self.dump_memory(file, obj = nil)
21
+ Log.info "Dumping #{obj} objects into #{ file }"
22
+ Thread.new do
23
+ while true
24
+ Open.write(file) do |f|
25
+ MUTEX_FOR_THREAD_EXCLUSIVE.synchronize do
26
+ GC.start
27
+ ObjectSpace.each_object(obj) do |o|
28
+ f.puts "---"
29
+ f.puts(String === o ? o : o.inspect)
30
+ end
31
+ end
32
+ end
33
+ FileUtils.cp file, file + '.save'
34
+ sleep 3
35
+ end
36
+ end
37
+ end
38
+
39
+ def self.file_time(file)
40
+ info = {}
41
+ begin
42
+ info[:ctime] = File.ctime file
43
+ info[:atime] = File.atime file
44
+ info[:elapsed] = Time.now - info[:ctime]
45
+ rescue Exception
46
+ end
47
+ info[:ctime] = Time.now - 999
48
+ info
49
+ end
50
+
51
+ #{{{ LOCKS
52
+
53
+ def self.locks(dirs = LOCK_DIRS)
54
+ dirs.collect do |dir|
55
+ next unless Open.exists? dir
56
+ `find -L "#{ dir }" -name "*.lock" 2>/dev/null`.split "\n"
57
+ end.compact.flatten
58
+ end
59
+
60
+ def self.lock_info(dirs = LOCK_DIRS)
61
+ lock_info = {}
62
+ locks(dirs).each do |f|
63
+ lock_info[f] = {}
64
+ begin
65
+ lock_info[f].merge!(file_time(f))
66
+ if File.size(f) > 0
67
+ info = Open.open(f) do |s|
68
+ Open.yaml(s)
69
+ end
70
+ IndiferentHash.setup(info)
71
+ lock_info[f][:pid] = info[:pid]
72
+ lock_info[f][:ppid] = info[:ppid]
73
+ end
74
+ rescue Exception
75
+ Log.warn $!.message
76
+ end
77
+ end
78
+ lock_info
79
+ end
80
+
81
+ #{{{ SENSIBLE WRITES
82
+
83
+ def self.sensiblewrites(dirs = SENSIBLE_WRITE_DIRS)
84
+ dirs.collect do |dir|
85
+ next unless Open.exists? dir
86
+ `find -L "#{ dir }" -not -name "*.lock" -not -type d 2>/dev/null`.split "\n"
87
+ end.compact.flatten
88
+ end
89
+
90
+ def self.sensiblewrite_info(dirs = SENSIBLE_WRITE_DIRS)
91
+ info = {}
92
+ sensiblewrites(dirs).each do |f|
93
+ begin
94
+ i = file_time(f)
95
+ info[f] = i
96
+ rescue
97
+ Log.exception $!
98
+ end
99
+ end
100
+ info
101
+ end
102
+
103
+ # PERSISTS
104
+
105
+ def self.persists(dirs = PERSIST_DIRS)
106
+ dirs.collect do |dir|
107
+ next unless Open.exists? dir
108
+ `find -L "#{ dir }" -name "*.persist" 2>/dev/null`.split "\n"
109
+ end.compact.flatten
110
+ end
111
+
112
+ def self.persist_info(dirs = PERSIST_DIRS)
113
+ info = {}
114
+ persists(dirs).each do |f|
115
+ begin
116
+ i = file_time(f)
117
+ info[f] = i
118
+ rescue
119
+ Log.exception $!
120
+ end
121
+ end
122
+ info
123
+ end
124
+
125
+ # PERSISTS
126
+
127
+ def self.job_info(workflows = nil, tasks = nil, dirs = JOB_DIRS)
128
+ require 'rbbt/workflow/step'
129
+
130
+ workflows = [workflows] if workflows and not Array === workflows
131
+ workflows = workflows.collect{|w| w.to_s} if workflows
132
+
133
+ tasks = [tasks] if tasks and not Array === tasks
134
+ tasks = tasks.collect{|w| w.to_s} if tasks
135
+
136
+ jobs = {}
137
+ seen = Set.new
138
+ _files = Set.new
139
+ dirs.collect do |dir|
140
+ next unless Open.exists? dir
141
+
142
+ task_dir_workflows = {}
143
+ tasks_dirs = if dir == '.'
144
+ ["."]
145
+ else
146
+ #workflowdirs = if (dir_sub_path = Open.find_repo_dir(workflowdir))
147
+ # repo_dir, sub_path = dir_sub_path
148
+ # Open.list_repo_files(*dir_sub_path).collect{|f| f.split("/").first}.uniq.collect{|f| File.join(repo_dir, f)}.uniq
149
+ # else
150
+ # dir.glob("*")
151
+ # end
152
+
153
+ workflowdirs = dir.glob("*")
154
+
155
+ workflowdirs.collect do |workflowdir|
156
+ workflow = File.basename(workflowdir)
157
+ next if workflows and not workflows.include? workflow
158
+
159
+ #task_dirs = if (dir_sub_path = Open.find_repo_dir(workflowdir))
160
+ # repo_dir, sub_path = dir_sub_path
161
+ # Open.list_repo_files(*dir_sub_path).collect{|f| f.split("/").first}.uniq.collect{|f| File.join(repo_dir, f)}.uniq
162
+ # else
163
+ # workflowdir.glob("*")
164
+ # end
165
+
166
+ task_dirs = workflowdir.glob("*")
167
+
168
+ task_dirs.each do |tasks_dir|
169
+ task_dir_workflows[tasks_dir] = workflow
170
+ end
171
+ end.compact.flatten
172
+ end
173
+
174
+ tasks_dirs.collect do |taskdir|
175
+ task = File.basename(taskdir)
176
+ next if tasks and not tasks.include? task
177
+
178
+
179
+ #files = if (dir_sub_path = Open.find_repo_dir(taskdir))
180
+ # repo_dir, sub_path = dir_sub_path
181
+ # Open.list_repo_files(*dir_sub_path).reject do |f|
182
+ # f.include?("/.info/") ||
183
+ # f.include?(".files/") ||
184
+ # f.include?(".pid/") ||
185
+ # File.directory?(f)
186
+ # end.collect do |f|
187
+ # File.join(repo_dir, f)
188
+ # end
189
+ # else
190
+ # #cmd = "find -L '#{ taskdir }/' -not \\( -path \"#{taskdir}/*.files/*\" -prune \\) -not -name '*.pid' -not -name '*.notify' -not -name '\\.*' 2>/dev/null"
191
+ # cmd = "find -L '#{ taskdir }/' -not \\( -path \"#{taskdir}/.info/*\" -prune \\) -not \\( -path \"#{taskdir}/*.files/*\" -prune \\) -not -name '*.pid' -not -name '*.md5' -not -name '*.notify' -not -name '\\.*' \\( -not -type d -o -name '*.files' \\) 2>/dev/null"
192
+
193
+ # CMD.cmd(cmd, :pipe => true).read.split("\n")
194
+ # end
195
+
196
+ files = begin
197
+ cmd = "find -L '#{ taskdir }/' -not \\( -path \"#{taskdir}/.info/*\" -prune \\) -not \\( -path \"#{taskdir}/*.files/*\" -prune \\) -not -name '*.pid' -not -name '*.md5' -not -name '*.notify' -not -name '\\.*' \\( -not -type d -o -name '*.files' \\) 2>/dev/null"
198
+
199
+ CMD.cmd(cmd, :pipe => true).read.split("\n")
200
+ end
201
+
202
+ files = files.sort_by{|f| Open.mtime(f) || Time.now}
203
+ workflow = task_dir_workflows[taskdir]
204
+ TSV.traverse files, :type => :array, :into => jobs, :_bar => "Finding jobs in #{ taskdir }" do |file|
205
+ _files << file
206
+ if m = file.match(/(.*)\.(info|pid|files)$/)
207
+ file = m[1]
208
+ end
209
+ next if seen.include? file
210
+ seen << file
211
+
212
+ name = file[taskdir.length+1..-1]
213
+ info_file = file + '.info'
214
+
215
+ info = {}
216
+
217
+ info[:workflow] = workflow
218
+ info[:task] = task
219
+ info[:name] = name
220
+
221
+ if Open.exists? file
222
+ info = info.merge(file_time(file))
223
+ info[:done] = true
224
+ info[:info_file] = Open.exist?(info_file) ? info_file : nil
225
+ else
226
+ info = info.merge({:info_file => info_file, :done => false})
227
+ end
228
+
229
+ [file, info]
230
+ end
231
+
232
+ end.compact.flatten
233
+ end.compact.flatten
234
+ jobs
235
+ end
236
+
237
+ # REST
238
+
239
+ def self.__jobs(dirs = JOB_DIRS)
240
+ job_files = {}
241
+ dirs.each do |dir|
242
+ workflow_dirs = dir.glob("*").each do |wdir|
243
+ workflow = File.basename(wdir)
244
+ job_files[workflow] = {}
245
+ task_dirs = wdir.glob('*')
246
+ task_dirs.each do |tdir|
247
+ task = File.basename(tdir)
248
+ job_files[workflow][task] = tdir.glob('*')
249
+ end
250
+ end
251
+ end
252
+ jobs = {}
253
+ job_files.each do |workflow,task_jobs|
254
+ jobs[workflow] ||= {}
255
+ task_jobs.each do |task, files|
256
+ jobs[workflow][task] ||= {}
257
+ files.each do |f|
258
+ next if f =~ /\.lock$/
259
+ job = f.sub(/\.(info|files)/,'')
260
+
261
+ jobs[workflow][task][job] ||= {}
262
+ if jobs[workflow][task][job][:status].nil?
263
+ status = nil
264
+ status = :done if Open.exists? job
265
+ if status.nil? and f=~/\.info/
266
+ info = begin
267
+ Step::INFO_SERIALIZER.load(Open.read(f, :mode => 'rb'))
268
+ rescue
269
+ {}
270
+ end
271
+ status = info[:status]
272
+ pid = info[:pid]
273
+ end
274
+
275
+ jobs[workflow][task][job][:pid] = pid if pid
276
+ jobs[workflow][task][job][:status] = status if status
277
+ end
278
+ end
279
+ end
280
+ end
281
+ jobs
282
+ end
283
+
284
+ def self.load_lock(lock)
285
+ begin
286
+ info = Misc.insist 3 do
287
+ Open.yaml(lock)
288
+ end
289
+ info.values_at "pid", "ppid", "time"
290
+ rescue Exception
291
+ time = begin
292
+ File.atime(lock)
293
+ rescue Exception
294
+ Time.now
295
+ end
296
+ [nil, nil, time]
297
+ end
298
+ end
299
+
300
+ end
@@ -9,7 +9,7 @@ class PackedIndex
9
9
  }
10
10
 
11
11
  def self.process_mask(mask)
12
- str = ""
12
+ str = "".dup
13
13
  size = 0
14
14
  mask.each do |e|
15
15
  if ELEMS.include? e
@@ -50,7 +50,7 @@ class PackedIndex
50
50
  @mask = @stream.read(mask_length)
51
51
  @offset = @mask.length + 8
52
52
  end
53
- @nil_string = "NIL" << ("-" * (@item_size - 3))
53
+ @nil_string = "NIL" + ("-" * (@item_size - 3))
54
54
  end
55
55
 
56
56
  def file
@@ -37,7 +37,7 @@ class Sharder
37
37
  databases[shard]
38
38
  else
39
39
  database = databases[shard] ||= begin
40
- path = File.join(persistence_path, 'shard-' << shard.to_s)
40
+ path = File.join(persistence_path, 'shard-' + shard.to_s)
41
41
  (writable or File.exist?(path)) ? Persist.open_database(path, (File.exist?(path) ? false : writable), :clean, db_type, @persist_options) : nil
42
42
  end
43
43
  Log.warn "Database #{ path } missing" if database.nil?
@@ -63,6 +63,7 @@ module Persist
63
63
 
64
64
  yield(data)
65
65
  data.save_annotation_hash if Annotation.is_annotated?(data)
66
+ data.read if data.respond_to?(:read)
66
67
  data
67
68
  end
68
69
  end
@@ -75,7 +75,7 @@ if continue
75
75
 
76
76
  def self.with_semaphore(size, file = nil)
77
77
  if file.nil?
78
- file = "/scout-" << Misc.digest(rand(100000000000).to_s)[0..10] if file.nil?
78
+ file = "/scout-" + Misc.digest(rand(100000000000).to_s)[0..10] if file.nil?
79
79
  else
80
80
  file = file.gsub('/', '_') if file
81
81
  end
@@ -16,7 +16,7 @@ module TSV
16
16
  if String === preamble
17
17
  preamble_str = preamble
18
18
  elsif preamble && options.values.compact.any?
19
- preamble_str = "#: " << IndiferentHash.hash2string(options.merge(serializer: nil))
19
+ preamble_str = "#: " + IndiferentHash.hash2string(options.merge(serializer: nil))
20
20
  else
21
21
  preamble_str = nil
22
22
  end
@@ -81,7 +81,7 @@ module TSV
81
81
  header = Dumper.header(@options.merge(type: @type, sep: @sep, preamble: preamble))
82
82
  @mutex.synchronize do
83
83
  @initialized = true
84
- @sin << header << "\n" if header and ! header.empty?
84
+ @sin << header + "\n" if header and ! header.empty?
85
85
  end
86
86
  end
87
87
 
@@ -134,7 +134,7 @@ module TSV
134
134
  end
135
135
 
136
136
  def fingerprint
137
- "Dumper:{"<< Log.fingerprint(self.all_fields|| []) << "}"
137
+ "Dumper:{" + Log.fingerprint(self.all_fields|| []) << "}"
138
138
  end
139
139
 
140
140
  def digest_str
@@ -61,6 +61,7 @@ module Open
61
61
  end
62
62
 
63
63
  if into.respond_to?(:close)
64
+ obj = obj.find if Path === obj
64
65
  into_thread = Thread.new do
65
66
  Thread.current.report_on_exception = false
66
67
  Thread.current["name"] = "Traverse into"
@@ -447,7 +447,7 @@ module TSV
447
447
  end
448
448
 
449
449
  def fingerprint
450
- "Parser:{"<< Log.fingerprint(self.all_fields|| []) << "}"
450
+ "Parser:{" + Log.fingerprint(self.all_fields|| []) << "}"
451
451
  end
452
452
 
453
453
  def digest_str
@@ -3,6 +3,7 @@ module TSV
3
3
  attr_accessor :unnamed, :parser, :dumper, :namespace
4
4
 
5
5
  def initialize(parser, dumper = nil, unnamed: nil, namespace: nil)
6
+
6
7
  if TSV::Parser === parser
7
8
  @parser = parser
8
9
  elsif TSV === parser
@@ -165,11 +165,11 @@ Example:
165
165
  end
166
166
 
167
167
  def fingerprint
168
- "TSV:{"<< Log.fingerprint(self.all_fields|| []) << ";" << Log.fingerprint(self.keys) << "}"
168
+ "TSV:{" + Log.fingerprint(self.all_fields|| []) << ";" << Log.fingerprint(self.keys) << "}"
169
169
  end
170
170
 
171
171
  def digest_str
172
- "TSV:{"<< Log.fingerprint(self.all_fields|| []) << ";" << Log.fingerprint(self.keys) << ";" << Log.fingerprint(self.values) << "}"
172
+ "TSV:{" + Log.fingerprint(self.all_fields|| []) << ";" << Log.fingerprint(self.keys) << ";" << Log.fingerprint(self.values) << "}"
173
173
  end
174
174
 
175
175
  def inspect
@@ -9,7 +9,7 @@ class WorkQueue
9
9
 
10
10
  @serializer = serializer || Marshal
11
11
 
12
- @key = "/" << rand(1000000000).to_s << '.' << Process.pid.to_s;
12
+ @key = "/" + rand(1000000000).to_s << '.' << Process.pid.to_s;
13
13
  @write_sem = @key + '.in'
14
14
  @read_sem = @key + '.out'
15
15
  Log.debug "Creating socket semaphores: #{@key}"
@@ -27,9 +27,9 @@ class WorkQueue
27
27
  run do
28
28
  begin
29
29
  if output
30
- Open.purge_pipes(output.swrite)
30
+ Open.purge_pipes(input.sread, output.swrite)
31
31
  else
32
- Open.purge_pipes
32
+ Open.purge_pipes(input.sread)
33
33
  end
34
34
 
35
35
  while obj = input.read
@@ -43,9 +43,11 @@ class WorkQueue
43
43
  rescue DoneProcessing
44
44
  rescue Interrupt
45
45
  rescue Exception
46
- output.write WorkerException.new($!, Process.pid)
47
- exit -1
48
- ensure
46
+ begin
47
+ output.write WorkerException.new($!, Process.pid)
48
+ ensure
49
+ exit -1
50
+ end
49
51
  end
50
52
  exit 0
51
53
  end
@@ -2,7 +2,7 @@ module Workflow
2
2
  attr_accessor :title, :description
3
3
 
4
4
  def self.doc_parse_first_line(str)
5
- if str.match(/^([^\n]*)\n\n(.*)/sm)
5
+ if str.match(/^([^\n]*)\n\n(.*)/smu)
6
6
  str.replace $2
7
7
  $1
8
8
  else
@@ -71,10 +71,31 @@ module EntityWorkflow
71
71
  end
72
72
 
73
73
  property_name = task_name.to_s.sub(/^(#{entity_name}_list|#{entity_name}|list)_/, '')
74
+ property_job_name = property_name + '_job'
75
+
76
+ property property_job_name => property_type do |*args|
77
+ job(task_name, *args)
78
+ end
79
+
74
80
  property property_name => property_type do |*args|
75
- job = job(task_name, *args)
81
+ job = self.send(property_job_name)
82
+
83
+ job.join if job.running?
84
+
85
+ if job.error?
86
+ if job.recoverable_error?
87
+ job.clean
88
+ else
89
+ raise job.exception
90
+ end
91
+ end
92
+
93
+ job.run unless job.done?
94
+
95
+ job.load
76
96
  Array === job ? job.collect(&:run) : job.run
77
97
  end
98
+
78
99
  end
79
100
 
80
101
  def entity_task(task_name, *args, &block)
@@ -8,10 +8,10 @@ class Step
8
8
  new_tokens = []
9
9
  if workflow
10
10
  workflow_name = workflow.name
11
- new_tokens << ("workflow:" << workflow_name)
12
- new_tokens << ("task:" << workflow_name << "#" << task_name.to_s)
11
+ new_tokens << ("workflow:" + workflow_name)
12
+ new_tokens << ("task:" + workflow_name << "#" << task_name.to_s)
13
13
  end
14
- new_tokens << ("task:" << task_name.to_s)
14
+ new_tokens << ("task:" + task_name.to_s)
15
15
 
16
16
  Scout::Config.get(key, tokens + new_tokens, options)
17
17
  end
@@ -13,6 +13,10 @@ class Step
13
13
  end
14
14
  end
15
15
 
16
+ def files_dir=(dir)
17
+ @files_dir = dir
18
+ end
19
+
16
20
  def file(file = nil)
17
21
  dir = files_dir
18
22
  Path.setup(dir) unless Path === dir
@@ -120,7 +120,7 @@ class Step
120
120
  if info.include?(key)
121
121
  case info[key]
122
122
  when Array
123
- info[key].concat Array === value ? value : [value]
123
+ info[key].concat(Array === value ? value : [value])
124
124
  when Hash
125
125
  info[key].merge! value
126
126
  else
@@ -201,7 +201,13 @@ class Step
201
201
  end
202
202
 
203
203
  def exception
204
- info[:exception]
204
+ return nil unless info[:exception]
205
+ begin
206
+ Marshal.load(Base64.decode64(info[:exception]))
207
+ rescue
208
+ Log.exception $!
209
+ nil
210
+ end
205
211
  end
206
212
 
207
213
  # Marshal Step
@@ -16,7 +16,7 @@ require_relative 'step/archive'
16
16
  class Step
17
17
 
18
18
  attr_accessor :path, :inputs, :dependencies, :id, :task, :tee_copies, :non_default_inputs, :provided_inputs, :compute, :overriden_task, :overriden_workflow, :workflow, :exec_context, :overriden
19
- def initialize(path = nil, inputs = nil, dependencies = nil, id = nil, non_default_inputs = nil, provided_inputs = nil, compute = nil, exec_context = nil, &task)
19
+ def initialize(path = nil, inputs = nil, dependencies = nil, id = nil, non_default_inputs = nil, provided_inputs = nil, compute = nil, exec_context: nil, &task)
20
20
  @path = path
21
21
  @inputs = inputs
22
22
  @dependencies = dependencies
@@ -112,17 +112,22 @@ class Step
112
112
 
113
113
  def exec
114
114
 
115
- if inputs
115
+ if inputs
116
116
  if Task === task
117
117
  types = task.inputs.collect{|name,type| type }
118
- new_inputs = inputs.zip(types).collect{|input,info|
118
+ new_inputs = inputs.zip(types).collect{|input,info|
119
119
  type, desc, default, options = info
120
120
  next input unless Step === input
121
121
  input.join if input.streaming?
122
122
  Task.format_input(input.join.path, type, options)
123
123
  }
124
124
  else
125
- new_inputs = inputs.collect{|input|
125
+ if Hash === inputs
126
+ new_inputs = inputs.values
127
+ else
128
+ new_inputs = inputs
129
+ end
130
+ new_inputs = new_inputs.collect{|input|
126
131
  Step === input ? input.load : input
127
132
  }
128
133
  end
@@ -218,7 +223,7 @@ class Step
218
223
 
219
224
  @result
220
225
  rescue Exception => e
221
- merge_info :status => :error, :exception => e, :end => Time.now, :backtrace => e.backtrace, :message => "#{e.class}: #{e.message}"
226
+ merge_info :status => :error, :exception => Base64.encode64(Marshal.dump(e)), :end => Time.now, :backtrace => e.backtrace, :message => "#{e.class}: #{e.message}"
222
227
  begin
223
228
  abort_dependencies
224
229
  ensure
@@ -169,7 +169,7 @@ module Task
169
169
  end
170
170
 
171
171
  def load_inputs(directory)
172
- if Open.exists?(directory) && ! Open.directory?(directory)
172
+ if Open.exists?(directory) && ! Open.directory?(directory) && ! Open.size(directory) == 0
173
173
  TmpFile.with_file do |tmp_directory|
174
174
  Misc.in_dir tmp_directory do
175
175
  CMD.cmd("tar xvfz '#{directory}'")