rbbt-util 5.2.4 → 5.3.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 (47) hide show
  1. checksums.yaml +8 -8
  2. data/bin/rbbt +23 -10
  3. data/bin/rbbt_monitor.rb +8 -8
  4. data/lib/rbbt/annotations.rb +22 -1
  5. data/lib/rbbt/annotations/util.rb +1 -1
  6. data/lib/rbbt/entity.rb +162 -0
  7. data/lib/rbbt/fix_width_table.rb +7 -0
  8. data/lib/rbbt/persist.rb +16 -9
  9. data/lib/rbbt/persist/tsv.rb +14 -8
  10. data/lib/rbbt/resource.rb +1 -6
  11. data/lib/rbbt/resource/path.rb +23 -27
  12. data/lib/rbbt/tsv.rb +33 -4
  13. data/lib/rbbt/tsv/accessor.rb +100 -57
  14. data/lib/rbbt/tsv/attach.rb +3 -1
  15. data/lib/rbbt/tsv/attach/util.rb +34 -10
  16. data/lib/rbbt/tsv/index.rb +12 -3
  17. data/lib/rbbt/tsv/manipulate.rb +25 -1
  18. data/lib/rbbt/tsv/parser.rb +1 -0
  19. data/lib/rbbt/util/R.rb +36 -6
  20. data/lib/rbbt/util/cmd.rb +2 -1
  21. data/lib/rbbt/util/color.rb +250 -0
  22. data/lib/rbbt/util/colorize.rb +57 -0
  23. data/lib/rbbt/util/misc.rb +57 -19
  24. data/lib/rbbt/util/named_array.rb +66 -14
  25. data/lib/rbbt/util/open.rb +134 -10
  26. data/lib/rbbt/util/semaphore.rb +71 -0
  27. data/lib/rbbt/workflow.rb +34 -7
  28. data/lib/rbbt/workflow/accessor.rb +12 -8
  29. data/lib/rbbt/workflow/step.rb +44 -28
  30. data/lib/rbbt/workflow/usage.rb +3 -0
  31. data/share/lib/R/util.R +31 -0
  32. data/share/rbbt_commands/app/start +5 -4
  33. data/share/rbbt_commands/study/task +222 -0
  34. data/share/rbbt_commands/tsv/attach +13 -0
  35. data/share/rbbt_commands/tsv/change_id +15 -0
  36. data/share/rbbt_commands/tsv/info +3 -1
  37. data/share/rbbt_commands/workflow/task +14 -15
  38. data/test/rbbt/test_entity.rb +221 -0
  39. data/test/rbbt/test_tsv.rb +2 -1
  40. data/test/rbbt/test_workflow.rb +0 -2
  41. data/test/rbbt/tsv/test_accessor.rb +2 -2
  42. data/test/rbbt/util/test_R.rb +9 -2
  43. data/test/rbbt/util/test_colorize.rb +12 -0
  44. data/test/rbbt/util/test_misc.rb +0 -5
  45. data/test/rbbt/util/test_open.rb +31 -0
  46. data/test/rbbt/workflow/test_step.rb +32 -0
  47. metadata +13 -2
@@ -0,0 +1,71 @@
1
+ require 'inline'
2
+
3
+ module RbbtSemaphore
4
+ inline(:C) do |builder|
5
+ builder.prefix <<-EOF
6
+ #include <unistd.h>
7
+ #include <stdio.h>
8
+ #include <stdlib.h>
9
+ #include <semaphore.h>
10
+ #include <time.h>
11
+ #include <assert.h>
12
+ #include <errno.h>
13
+ #include <signal.h>
14
+ #include <fcntl.h>
15
+ EOF
16
+
17
+ builder.c_singleton <<-EOF
18
+ void create_semaphore(char* name, int value){
19
+ sem_open(name, O_CREAT, S_IRWXU, value);
20
+ }
21
+ EOF
22
+ builder.c_singleton <<-EOF
23
+ void delete_semaphore(char* name){
24
+ sem_unlink(name);
25
+ }
26
+ EOF
27
+
28
+ builder.c_singleton <<-EOF
29
+ void wait_semaphore(char* name){
30
+ sem_t* sem;
31
+ sem = sem_open(name, O_EXCL);
32
+ sem_wait(sem);
33
+ sem_close(sem);
34
+ }
35
+ EOF
36
+
37
+ builder.c_singleton <<-EOF
38
+ void post_semaphore(char* name){
39
+ sem_t* sem;
40
+ sem = sem_open(name, O_EXCL);
41
+ sem_post(sem);
42
+ sem_close(sem);
43
+ }
44
+ EOF
45
+ end
46
+
47
+ def self.with_semaphore(size, file = nil)
48
+ file = Misc.digest(rand.to_s) if file.nil?
49
+ file.gsub!('/', '_')
50
+ begin
51
+ RbbtSemaphore.create_semaphore(file, size)
52
+ yield file
53
+ ensure
54
+ RbbtSemaphore.delete_semaphore(file)
55
+ end
56
+ end
57
+
58
+ def self.fork_each_on_semaphore(elems, size, file = nil)
59
+ with_semaphore(size, file) do |file|
60
+ pids = elems.collect do |elem|
61
+ Process.fork do
62
+ RbbtSemaphore.wait_semaphore(file)
63
+ yield elem
64
+ RbbtSemaphore.post_semaphore(file)
65
+ end
66
+ end
67
+ pids.each do |pid| Process.waitpid pid end
68
+ end
69
+ end
70
+ end
71
+
data/lib/rbbt/workflow.rb CHANGED
@@ -58,8 +58,9 @@ module Workflow
58
58
  else
59
59
  case
60
60
  # Points to workflow file
61
- when ((File.exists?(wf_name) and not File.directory?(wf_name)) or File.exists?(wf_name + '.rb'))
61
+ when ((File.exists?(wf_name) and not File.directory?(wf_name)) or File.exists?(wf_name + '.rb') or File.exists?(wf_name))
62
62
  $LOAD_PATH.unshift(File.join(File.expand_path(File.dirname(wf_name)), 'lib'))
63
+ wf_name = "./" << wf_name unless wf_name[0] == "/"
63
64
  require wf_name
64
65
  Log.medium "Workflow loaded from file: #{ wf_name }"
65
66
  return true
@@ -112,19 +113,17 @@ module Workflow
112
113
  begin
113
114
  require_local_workflow(wf_name)
114
115
  rescue Exception
115
- Log.debug $!.message
116
- Log.debug $!.backtrace.first
117
116
  raise "Workflow not found: #{ wf_name }" if wf_name == Misc.snake_case(wf_name)
118
- Log.debug "Trying with humanized: '#{Misc.snake_case wf_name}'"
119
117
  begin
120
118
  require_local_workflow(Misc.snake_case(wf_name))
121
119
  rescue Exception
122
- Log.debug $!.message
123
- raise "Workflow not found: #{ wf_name }"
120
+ Log.error("Workflow not found: #{ wf_name }")
121
+ raise $!
124
122
  end
125
123
  end
126
124
  end
127
125
 
126
+ attr_accessor :description
128
127
  attr_accessor :libdir, :workdir
129
128
  attr_accessor :helpers, :tasks
130
129
  attr_accessor :task_dependencies, :task_description, :last_task
@@ -134,7 +133,8 @@ module Workflow
134
133
 
135
134
  def workdir
136
135
  @workdir ||= if defined? Rbbt
137
- Rbbt.var.jobs[self].find
136
+ text = Module === self ? self.to_s : "Misc"
137
+ Rbbt.var.jobs[text].find
138
138
  else
139
139
  Path.setup('var/jobs')
140
140
  end
@@ -144,6 +144,17 @@ module Workflow
144
144
  @libdir = Path.caller_lib_dir if @libdir.nil?
145
145
  @libdir
146
146
  end
147
+
148
+ def workflow_description
149
+ @workflow_description ||= begin
150
+ file = @libdir['workflow.md']
151
+ if file.exists?
152
+ file.read
153
+ else
154
+ ""
155
+ end
156
+ end
157
+ end
147
158
 
148
159
  def helpers
149
160
  @helpers ||= {}
@@ -232,4 +243,20 @@ module Workflow
232
243
  Misc.path_relative_to(task_dir, f).sub(".info",'')
233
244
  }
234
245
  end
246
+
247
+ def local_persist_setup
248
+ class << self
249
+ include LocalPersist
250
+ end
251
+ self.local_persist_dir = Rbbt.var.cache.persistence.find :lib
252
+ end
253
+
254
+ def local_workdir_setup
255
+ self.workdir = Rbbt.var.jobs.find :lib
256
+ end
257
+
258
+ def make_local
259
+ local_persist_setup
260
+ local_workdir_setup
261
+ end
235
262
  end
@@ -2,6 +2,8 @@ require 'rbbt/util/open'
2
2
  require 'yaml'
3
3
 
4
4
  class Step
5
+
6
+ INFO_SERIALIAZER = Marshal
5
7
 
6
8
  def name
7
9
  @path.sub(/.*\/#{Regexp.quote task.name.to_s}\/(.*)/, '\1')
@@ -22,22 +24,24 @@ class Step
22
24
  end
23
25
 
24
26
  def info
25
- return {} if not File.exists? info_file
27
+ return {} if not Open.exists? info_file
26
28
  begin
27
- File.open(info_file) do |file|
28
- YAML.load(file) || {}
29
+ Open.open(info_file) do |file|
30
+ INFO_SERIALIAZER.load(file) || {}
29
31
  end
30
32
  rescue Exception
31
- Log.debug "Error loading yaml: " + info_file
33
+ Log.debug "Error loading info file: " + info_file
32
34
  raise $!
33
35
  end
34
36
  end
35
37
 
36
38
  def set_info(key, value)
37
- Misc.lock(info_file) do
39
+ return nil if @exec
40
+ value = Annotated.purge value
41
+ Open.lock(info_file) do
38
42
  i = info
39
43
  i[key] = value
40
- Open.write(info_file, i.to_yaml)
44
+ Open.write(info_file, INFO_SERIALIAZER.dump(i))
41
45
  value
42
46
  end
43
47
  end
@@ -71,7 +75,7 @@ class Step
71
75
  end
72
76
 
73
77
  def started?
74
- File.exists? info_file
78
+ Open.exists? info_file
75
79
  end
76
80
 
77
81
  def done?
@@ -79,7 +83,7 @@ class Step
79
83
  end
80
84
 
81
85
  def running?
82
- return nil if not File.exists? info_file
86
+ return nil if not Open.exists? info_file
83
87
  return nil if info[:pid].nil?
84
88
  return Misc.pid_exists? info[:pid]
85
89
  end
@@ -1,11 +1,13 @@
1
1
  require 'rbbt/persist'
2
2
  require 'rbbt/persist/tsv'
3
3
  require 'rbbt/util/log'
4
+ require 'rbbt/util/semaphore'
4
5
  require 'rbbt/workflow/accessor'
5
6
 
6
7
  class Step
7
8
  attr_accessor :path, :task, :inputs, :dependencies, :bindings
8
9
  attr_accessor :pid
10
+ attr_accessor :exec
9
11
 
10
12
  class Aborted < Exception; end
11
13
 
@@ -62,6 +64,7 @@ class Step
62
64
  end
63
65
 
64
66
  def exec
67
+ @exec = true if @exec.nil?
65
68
  result = @task.exec_in((bindings ? bindings : self), *@inputs)
66
69
  prepare_result result, @task.result_description
67
70
  end
@@ -83,11 +86,12 @@ class Step
83
86
 
84
87
  def run(no_load = false)
85
88
  result = Persist.persist "Job", @task.result_type, :file => @path, :check => rec_dependencies.collect{|dependency| dependency.path }.uniq, :no_load => no_load do
89
+ @exec = false
86
90
  if Step === Step.log_relay_step and not self == Step.log_relay_step
87
91
  relay_log(Step.log_relay_step) unless self.respond_to? :relay_step and self.relay_step
88
92
  end
89
93
 
90
- FileUtils.rm info_file if File.exists? info_file
94
+ Open.rm info_file if Open.exists? info_file
91
95
 
92
96
  set_info :pid, Process.pid
93
97
 
@@ -95,6 +99,7 @@ class Step
95
99
  dependencies.each{|dependency|
96
100
  begin
97
101
  dependency.relay_log self
102
+ dependency.clean if not dependency.done? and dependency.error?
98
103
  dependency.run true
99
104
  rescue Exception
100
105
  backtrace = $!.backtrace
@@ -158,37 +163,43 @@ class Step
158
163
  end
159
164
  end
160
165
 
161
- def fork
166
+ def fork(semaphore = nil)
162
167
  raise "Can not fork: Step is waiting for proces #{@pid} to finish" if not @pid.nil?
163
168
  @pid = Process.fork do
164
169
  trap(:INT) { raise Step::Aborted.new "INT signal recieved" }
165
- FileUtils.mkdir_p File.dirname(path) unless File.exists? File.dirname(path)
166
170
  begin
167
- run
168
- rescue Exception
169
- Log.debug("Exception caught on forked process: #{$!.message}")
170
- exit -1
171
- end
171
+ RbbtSemaphore.wait_semaphore(semaphore) if semaphore
172
+ FileUtils.mkdir_p File.dirname(path) unless Open.exists? File.dirname(path)
173
+ begin
174
+ run(true)
175
+ rescue Exception
176
+ Log.debug("Exception caught on forked process: #{$!.message}")
177
+ exit -1
178
+ end
172
179
 
173
- begin
174
- children_pids = info[:children_pids]
175
- if children_pids
176
- children_pids.each do |pid|
177
- if Misc.pid_exists? pid
178
- begin
179
- Process.waitpid pid
180
- rescue Errno::ECHILD
181
- Log.error "Waiting on #{ pid } failed: #{$!.message}"
180
+ begin
181
+ children_pids = info[:children_pids]
182
+ if children_pids
183
+ children_pids.each do |pid|
184
+ if Misc.pid_exists? pid
185
+ begin
186
+ Process.waitpid pid
187
+ rescue Errno::ECHILD
188
+ Log.error "Waiting on #{ pid } failed: #{$!.message}"
189
+ end
182
190
  end
183
191
  end
192
+ set_info :children_done, Time.now
184
193
  end
194
+ rescue Exception
195
+ Log.debug("Exception waiting for children: #{$!.message}")
196
+ exit -1
185
197
  end
186
- rescue Exception
187
- Log.debug("Exception waiting for children: #{$!.message}")
188
- exit -1
198
+ set_info :pid, nil
199
+ exit 0
200
+ ensure
201
+ RbbtSemaphore.post_semaphore(semaphore) if semaphore
189
202
  end
190
- set_info :pid, nil
191
- exit 0
192
203
  end
193
204
  Process.detach(@pid)
194
205
  self
@@ -234,13 +245,13 @@ class Step
234
245
  end
235
246
 
236
247
  def clean
237
- if File.exists?(path) or File.exists?(info_file)
248
+ if Open.exists?(path) or Open.exists?(info_file)
238
249
  begin
239
- FileUtils.rm info_file if File.exists? info_file
240
- FileUtils.rm info_file + '.lock' if File.exists? info_file + '.lock'
241
- FileUtils.rm path if File.exists? path
242
- FileUtils.rm path + '.lock' if File.exists? path + '.lock'
243
- FileUtils.rm_rf files_dir if File.exists? files_dir
250
+ Open.rm info_file if Open.exists? info_file
251
+ Open.rm info_file + '.lock' if Open.exists? info_file + '.lock'
252
+ Open.rm path if Open.exists? path
253
+ Open.rm path + '.lock' if Open.exists? path + '.lock'
254
+ Open.rm_rf files_dir if Open.exists? files_dir
244
255
  end
245
256
  end
246
257
  self
@@ -250,6 +261,11 @@ class Step
250
261
  @dependencies.collect{|step| step.rec_dependencies}.flatten.concat @dependencies
251
262
  end
252
263
 
264
+ def recursive_clean
265
+ rec_dependencies.each{|step| step.clean }
266
+ clean
267
+ end
268
+
253
269
  def step(name)
254
270
  rec_dependencies.select{|step| step.task.name.to_sym == name.to_sym}.first
255
271
  end
@@ -15,6 +15,7 @@ module Task
15
15
  puts " #{dep.name}:"
16
16
  puts
17
17
  puts SOPT.input_doc(dep.inputs, dep.input_types, dep.input_descriptions, dep.input_defaults)
18
+ puts
18
19
  end
19
20
  end
20
21
  end
@@ -27,6 +28,8 @@ module Workflow
27
28
  puts self.to_s
28
29
  puts "=" * self.to_s.length
29
30
  puts
31
+ puts "\n" << workflow_description if workflow_description and not workflow_description.empty?
32
+ puts
30
33
 
31
34
  puts "## TASKS"
32
35
  puts
data/share/lib/R/util.R CHANGED
@@ -167,7 +167,38 @@ rbbt.acc <- function(data, new){
167
167
  rbbt.png_plot <- function(filename, width, height, p, ...){
168
168
  png(filename=filename, width=width, height=height, ...);
169
169
  eval(parse(text=p));
170
+ }
171
+
172
+ rbbt.heatmap <- function(filename, width, height, data, take_log=FALSE, ...){
173
+ require(gplots, quietly = TRUE, warn.conflicts = FALSE)
174
+ library(pls, quietly = TRUE, warn.conflicts = FALSE)
175
+ opar = par()
176
+ png(filename=filename, width=width, height=height);
177
+
178
+ #par(cex.lab=0.5, cex=0.5, ...)
179
+
180
+ data = as.matrix(data)
181
+ data[is.nan(data)] = NA
182
+
183
+ #data = data[rowSums(!is.na(data))!=0, colSums(!is.na(data))!=0]
184
+ data = data[rowSums(is.na(data))==0, ]
185
+
186
+ if (take_log){
187
+ for (study in colnames(data)){
188
+ skip = sum(data[, study] <= 0) != 0
189
+ if (!skip){
190
+ data[, study] = log(data[, study])
191
+ }
192
+ }
193
+ data = data[, colSums(is.na(data))==0]
194
+ }
195
+
196
+ data = stdize(data)
197
+
198
+ heatmap.2(data, margins = c(20,5), scale='column')
199
+
170
200
  dev.off();
201
+ par(opar)
171
202
  }
172
203
 
173
204
  rbbt.init <- function(data, new){
@@ -3,7 +3,8 @@
3
3
  require 'rbbt-util'
4
4
  require 'rbbt/util/simpleopt'
5
5
 
6
- options = SOPT.get "-e--environment*:-p--port*:-s--server*"
6
+ options = SOPT.get "-e--environment*:-p--port*:-s--server*:-f--finder"
7
+ options[:Port] ||= options[:port]
7
8
 
8
9
  app = ARGV.shift
9
10
 
@@ -12,8 +13,8 @@ app_dir = Rbbt.etc.app_dir.exists? ? Path.setup(Rbbt.etc.app_dir.read.strip) : R
12
13
  app_dir = app_dir[app]
13
14
 
14
15
  server = options[:server] || 'thin'
15
-
16
16
  Misc.in_dir(app_dir) do
17
- `env RBBT_LOG=#{Log.severity} #{options.include?(:environment)? "env RACK_ENV=#{options[:environment]}" : ""} \
18
- #{server} start -p #{options[:port]} #{ARGV.collect{|a| "'#{a}'"} * " "}`
17
+ require 'rack'
18
+ ENV["RBBT_FINDER"] = true if options.include?(:finder)
19
+ Rack::Server.start(options.merge(:config => 'config.ru'))
19
20
  end
@@ -0,0 +1,222 @@
1
+ #!/usr/bin/env ruby
2
+
3
+
4
+ require 'rbbt/util/simpleopt'
5
+ require 'rbbt/workflow'
6
+ require 'rbbt/workflow/usage'
7
+ require 'rbbt/entity/study'
8
+ require 'rbbt/entity/study/genotypes'
9
+ require 'rbbt/entity/study/cnv'
10
+ require 'rbbt/entity/study/expression'
11
+
12
+ def usage(workflow = nil, task = nil)
13
+ puts SOPT.doc
14
+ puts "## WORKFLOW"
15
+ puts
16
+ if workflow.nil?
17
+ puts "No workflow specified"
18
+ exit -1
19
+ end
20
+
21
+ if task.nil?
22
+ workflow.load_tasks if workflow.respond_to? :load_tasks
23
+ workflow.doc
24
+ else
25
+ puts workflow.to_s
26
+ puts "=" * workflow.to_s.length
27
+ puts
28
+ puts workflow.workflow_description
29
+ puts
30
+ workflow.doc(task)
31
+ end
32
+
33
+ exit 0
34
+ end
35
+
36
+ def SOPT_options(workflow, task)
37
+ sopt_options = []
38
+ workflow.rec_inputs(task.name).each do |name|
39
+ short = name.to_s.chars.first
40
+ boolean = workflow.rec_input_types(task.name)[name].to_sym == :boolean
41
+
42
+ sopt_options << "-#{short}--#{name}#{boolean ? '' : '*'}"
43
+ end
44
+
45
+ sopt_options * ":"
46
+ end
47
+
48
+ def fix_options(workflow, task, job_options)
49
+ option_types = workflow.rec_input_types(task.name)
50
+
51
+ job_options_cleaned = {}
52
+
53
+ job_options.each do |name, value|
54
+ value = case option_types[name].to_sym
55
+ when :float
56
+ value.to_f
57
+ when :integer
58
+ value.to_i
59
+ when :string, :text
60
+ case
61
+ when value == '-'
62
+ STDIN.read
63
+ when (String === value and File.exists?(value) and not File.directory?(value))
64
+ Open.read(value)
65
+ else
66
+ value
67
+ end
68
+ when :array
69
+ if Array === value
70
+ value
71
+ else
72
+ str = case
73
+ when value == '-'
74
+ STDIN.read
75
+ when (String === value and File.exists?(value))
76
+ Open.read(value)
77
+ else
78
+ value
79
+ end
80
+
81
+ if $array_separator
82
+ str.split(/#{$array_separator}/)
83
+ else
84
+ str.split(/[,|\s]/)
85
+ end
86
+ end
87
+ when :tsv
88
+ case value
89
+ when TSV
90
+ value
91
+ when '-'
92
+ TSV.open(STDIN)
93
+ else
94
+ TSV.open(value)
95
+ end
96
+ else
97
+ value
98
+ end
99
+ job_options_cleaned[name] = value
100
+ end
101
+
102
+ job_options_cleaned
103
+ end
104
+
105
+ options = SOPT.get <<EOF
106
+ -h--help Show this help:
107
+ -as--array_separator* Change the character that separates elements of Arrays, ',', '|', or '\\n' by default:
108
+ -cl--clean Clean the last step of the job so that it gets recomputed:
109
+ -rcl--recursive_clean Clean the last step and its dependencies to recompute the job completely:
110
+ -n--name* Job name to use. The name 'Default' is used by default:
111
+ -pn--printname Print the name of the job and exit without starting it:
112
+ -rw--require_workflow* Workflows to require, separated by commas
113
+ EOF
114
+
115
+ (options[:require_workflow] || "").split(',').each do |workflow|
116
+ Workflow.require_workflow workflow
117
+ end
118
+
119
+ study = ARGV.shift.dup
120
+ if Open.exists? study
121
+ dir = study
122
+ study = Study.setup(File.basename(study))
123
+ study.dir = Path.setup(dir, Study)
124
+ else
125
+ Study.setup(study)
126
+ end
127
+ workflow = study.workflow
128
+
129
+ usage if workflow.nil?
130
+
131
+ task = ARGV.shift
132
+
133
+ # Set log, fork, clean, recursive_clean and help
134
+ help = !!options.delete(:help)
135
+ do_fork = !!options.delete(:fork)
136
+ do_exec = !!options.delete(:exec)
137
+ clean = !!options.delete(:clean)
138
+ recursive_clean = !!options.delete(:recursive_clean)
139
+ $array_separator = options.delete(:array_separator)
140
+
141
+ # Set task
142
+ namespace = nil, nil
143
+
144
+ case
145
+ when task.nil?
146
+ usage workflow
147
+ when (task =~ /\./)
148
+ namespace, task = options.delete(:task).split('.')
149
+ namespace = Misc.string2const(namespace)
150
+ else
151
+ task_name = task.to_sym
152
+ task = workflow.tasks[task_name]
153
+ raise "Task not found: #{ task_name }" if task.nil?
154
+ end
155
+
156
+ usage workflow, task if help
157
+
158
+ name = options.delete(:name) || "Default"
159
+
160
+ # get job args
161
+ sopt_option_string = SOPT_options(workflow, task)
162
+ job_options = SOPT.get sopt_option_string
163
+ job_options = fix_options(workflow, task, job_options)
164
+
165
+ #- get job
166
+
167
+ job = study.job(task.name, job_options)
168
+
169
+ # clean job
170
+ if clean and job.done? != false
171
+ job.clean
172
+ sleep 1
173
+ job = study.job(task.name, job_options)
174
+ end
175
+
176
+ if recursive_clean and job.done?
177
+ job.recursive_clean
178
+ sleep 1
179
+ job = study.job(task.name, job_options)
180
+ end
181
+
182
+ # run
183
+ if do_exec
184
+ res = job.exec
185
+ case
186
+ when Array === res
187
+ puts res * "\n"
188
+ when TSV === res
189
+ puts res
190
+ when Hash === res
191
+ puts res.to_yaml
192
+ else
193
+ puts res
194
+ end
195
+ exit 0
196
+ end
197
+
198
+ if do_fork
199
+ job.fork
200
+ while not job.done?
201
+ Log.debug "#{job.step}: #{job.messages.last}"
202
+ sleep 2
203
+ end
204
+ raise job.messages.last if job.error?
205
+ else
206
+ res = job.run(true)
207
+ end
208
+
209
+ if options.delete(:printname)
210
+ puts job.name
211
+ exit 0
212
+ else
213
+ Log.low "Job name: #{job.name}"
214
+ end
215
+
216
+ if Step === res
217
+ puts Open.read(res.path) if File.exists? res.path
218
+ else
219
+ puts res
220
+ end
221
+
222
+ exit 0