rbbt-util 5.28.10 → 5.29.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.
- checksums.yaml +4 -4
- data/lib/rbbt/hpc.rb +1 -549
- data/lib/rbbt/hpc/orchestrate.rb +24 -0
- data/lib/rbbt/hpc/slurm.rb +570 -0
- data/lib/rbbt/persist.rb +8 -3
- data/lib/rbbt/resource.rb +12 -6
- data/lib/rbbt/resource/path.rb +1 -1
- data/lib/rbbt/tsv/attach.rb +7 -4
- data/lib/rbbt/tsv/parallel.rb +0 -3
- data/lib/rbbt/util/R.rb +2 -2
- data/lib/rbbt/util/cmd.rb +9 -0
- data/lib/rbbt/util/misc/indiferent_hash.rb +8 -0
- data/lib/rbbt/util/misc/inspect.rb +23 -9
- data/lib/rbbt/workflow.rb +2 -1
- data/lib/rbbt/workflow/accessor.rb +8 -2
- data/lib/rbbt/workflow/definition.rb +1 -0
- data/lib/rbbt/workflow/examples.rb +2 -2
- data/lib/rbbt/workflow/step.rb +12 -6
- data/lib/rbbt/workflow/step/accessor.rb +47 -27
- data/lib/rbbt/workflow/step/dependencies.rb +9 -4
- data/lib/rbbt/workflow/step/run.rb +22 -20
- data/lib/rbbt/workflow/util/orchestrator.rb +14 -9
- data/lib/rbbt/workflow/util/provenance.rb +12 -5
- data/share/rbbt_commands/slurm/list +141 -0
- data/share/rbbt_commands/slurm/orchestrate +47 -0
- data/share/rbbt_commands/{workflow/slurm → slurm/task} +10 -3
- data/share/rbbt_commands/system/status +22 -22
- data/share/rbbt_commands/workflow/info +12 -9
- data/share/rbbt_commands/workflow/prov +2 -1
- data/test/rbbt/test_workflow.rb +36 -4
- data/test/rbbt/tsv/test_attach.rb +86 -6
- metadata +7 -3
data/lib/rbbt/persist.rb
CHANGED
@@ -26,12 +26,17 @@ module Persist
|
|
26
26
|
MAX_FILE_LENGTH = 150
|
27
27
|
|
28
28
|
# Is 'file' newer than 'path'? return non-true if path is newer than file
|
29
|
-
def self.newer?(path, file)
|
29
|
+
def self.newer?(path, file, by_link = false)
|
30
30
|
return true if not Open.exists?(file)
|
31
31
|
path = path.find if Path === path
|
32
32
|
file = file.find if Path === file
|
33
|
-
|
34
|
-
|
33
|
+
if by_link
|
34
|
+
patht = File.exists?(path) ? File.lstat(path).mtime : nil
|
35
|
+
filet = File.exists?(file) ? File.lstat(file).mtime : nil
|
36
|
+
else
|
37
|
+
patht = Open.mtime(path)
|
38
|
+
filet = Open.mtime(file)
|
39
|
+
end
|
35
40
|
return true if patht.nil? || filet.nil?
|
36
41
|
diff = patht - filet
|
37
42
|
return diff if diff < 0
|
data/lib/rbbt/resource.rb
CHANGED
@@ -112,13 +112,19 @@ module Resource
|
|
112
112
|
end
|
113
113
|
when Net::HTTPRedirection, Net::HTTPFound
|
114
114
|
location = response['location']
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
115
|
+
if location.include? 'get_directory'
|
116
|
+
Log.debug("Feching directory from: #{location}. Into: #{final_path}")
|
117
|
+
FileUtils.mkdir_p final_path unless File.exist? final_path
|
118
|
+
TmpFile.with_file do |tmp_dir|
|
119
|
+
Misc.in_dir tmp_dir do
|
120
|
+
CMD.cmd('tar xvfz -', :in => Open.open(location, :nocache => true))
|
121
|
+
end
|
122
|
+
FileUtils.mv tmp_dir, final_path
|
123
|
+
end
|
124
|
+
else
|
125
|
+
Open.open(location, :nocache => true) do |s|
|
126
|
+
Misc.sensiblewrite(final_path, s)
|
120
127
|
end
|
121
|
-
FileUtils.mv tmp_dir, final_path
|
122
128
|
end
|
123
129
|
when Net::HTTPInternalServerError
|
124
130
|
@server_missing_resource_cache << url
|
data/lib/rbbt/resource/path.rb
CHANGED
data/lib/rbbt/tsv/attach.rb
CHANGED
@@ -243,6 +243,7 @@ module TSV
|
|
243
243
|
Log.debug("Attachment of fields:#{Misc.fingerprint fields } from #{other.filename.inspect} finished.")
|
244
244
|
|
245
245
|
if complete
|
246
|
+
Log.warn "Attaching through index and completing empty rows; keys with wrong format may appear (#{other.key_field} insted of #{self.key_field})" if index
|
246
247
|
fill = TrueClass === complete ? nil : complete
|
247
248
|
field_length = self.fields.length
|
248
249
|
common_fields = (other.fields & self.fields)
|
@@ -255,11 +256,11 @@ module TSV
|
|
255
256
|
case type
|
256
257
|
when :single
|
257
258
|
missing.each do |k|
|
258
|
-
self[k] =
|
259
|
+
self[k] = fill
|
259
260
|
end
|
260
261
|
when :list
|
261
262
|
missing.each do |k|
|
262
|
-
values = [
|
263
|
+
values = [fill] * field_length
|
263
264
|
other_values = other[k]
|
264
265
|
other_common_pos.zip(this_common_pos).each do |o,t|
|
265
266
|
values[t] = other_values[o]
|
@@ -267,8 +268,9 @@ module TSV
|
|
267
268
|
self[k] = values
|
268
269
|
end
|
269
270
|
when :double
|
271
|
+
fill = [] if fill.nil?
|
270
272
|
missing.each do |k|
|
271
|
-
values = [
|
273
|
+
values = [fill] * field_length
|
272
274
|
other_values = other[k]
|
273
275
|
other_common_pos.zip(this_common_pos).each do |o,t|
|
274
276
|
values[t] = other_values[o]
|
@@ -276,8 +278,9 @@ module TSV
|
|
276
278
|
self[k] = values
|
277
279
|
end
|
278
280
|
when :flat
|
281
|
+
fill = [] if fill.nil?
|
279
282
|
missing.each do |k|
|
280
|
-
self[k] =
|
283
|
+
self[k] = fill
|
281
284
|
end
|
282
285
|
end
|
283
286
|
end
|
data/lib/rbbt/tsv/parallel.rb
CHANGED
data/lib/rbbt/util/R.rb
CHANGED
@@ -41,7 +41,7 @@ source('#{UTIL}');
|
|
41
41
|
|
42
42
|
if monitor
|
43
43
|
#io = CMD.cmd('R --no-save --quiet', options.merge(:in => cmd, :pipe => true, :log => true))
|
44
|
-
io = CMD.cmd('R --no-save --quiet', options.merge(:in => cmd, :pipe => true, :log => true))
|
44
|
+
io = CMD.cmd('R --no-save --quiet', options.merge(:in => cmd, :pipe => true, :log => true, :xvfb => true))
|
45
45
|
while line = io.gets
|
46
46
|
case monitor
|
47
47
|
when Proc
|
@@ -52,7 +52,7 @@ source('#{UTIL}');
|
|
52
52
|
end
|
53
53
|
nil
|
54
54
|
else
|
55
|
-
CMD.cmd('R --no-save --slave --quiet', options.merge(:in => cmd))
|
55
|
+
CMD.cmd('R --no-save --slave --quiet', options.merge(:in => cmd, :xvfb => true))
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
data/lib/rbbt/util/cmd.rb
CHANGED
@@ -100,6 +100,7 @@ module CMD
|
|
100
100
|
no_fail = options.delete(:no_fail)
|
101
101
|
no_fail = options.delete(:nofail) if no_fail.nil?
|
102
102
|
no_wait = options.delete(:no_wait)
|
103
|
+
xvfb = options.delete(:xvfb)
|
103
104
|
|
104
105
|
dont_close_in = options.delete(:dont_close_in)
|
105
106
|
|
@@ -117,6 +118,14 @@ module CMD
|
|
117
118
|
|
118
119
|
end
|
119
120
|
|
121
|
+
case xvfb
|
122
|
+
when TrueClass
|
123
|
+
cmd = "xvfb-run --server-args='-screen 0 1024x768x24' --auto-servernum #{cmd}"
|
124
|
+
when String
|
125
|
+
cmd = "xvfb-run --server-args='#{xvfb}' --auto-servernum --server-num=1 #{cmd}"
|
126
|
+
when String
|
127
|
+
end
|
128
|
+
|
120
129
|
if stderr == true
|
121
130
|
stderr = Log::HIGH
|
122
131
|
end
|
@@ -287,15 +287,21 @@ module Misc
|
|
287
287
|
when Symbol
|
288
288
|
obj.to_s
|
289
289
|
when (defined?(Path) and Path)
|
290
|
-
if
|
291
|
-
|
292
|
-
|
293
|
-
|
290
|
+
if defined?(Step) && Open.exists?(Step.info_file(obj))
|
291
|
+
obj2str(Workflow.load_step(obj))
|
292
|
+
elsif defined?(Step) && Step === obj.resource
|
293
|
+
"Step file: " + obj
|
294
|
+
else
|
295
|
+
if obj.exists?
|
296
|
+
if obj.directory?
|
297
|
+
files = obj.glob("**/*")
|
298
|
+
"directory: #{Misc.fingerprint(files)}"
|
299
|
+
else
|
300
|
+
"file: " << Open.realpath(obj) << "--" << mtime_str(obj)
|
301
|
+
end
|
294
302
|
else
|
295
|
-
|
303
|
+
obj + " (file missing)"
|
296
304
|
end
|
297
|
-
else
|
298
|
-
obj + " (file missing)"
|
299
305
|
end
|
300
306
|
when String
|
301
307
|
if Misc.is_filename?(obj) and ! %w(. ..).include?(obj)
|
@@ -318,7 +324,11 @@ module Misc
|
|
318
324
|
remove_long_items(obj)
|
319
325
|
when File
|
320
326
|
if obj.respond_to? :filename and obj.filename
|
321
|
-
|
327
|
+
if defined?(Step) && Open.exists?(Step.info_file(obj.filename))
|
328
|
+
obj2str(Workflow.load_step(obj.filename))
|
329
|
+
else
|
330
|
+
"<IO:" << obj.filename << "--" << mtime_str(obj.filename) << ">"
|
331
|
+
end
|
322
332
|
else
|
323
333
|
"<IO:" << obj.path << "--" << mtime_str(obj.path) << ">"
|
324
334
|
end
|
@@ -326,7 +336,11 @@ module Misc
|
|
326
336
|
"<IO:" << obj.short_path << ">"
|
327
337
|
when IO
|
328
338
|
if obj.respond_to? :filename and obj.filename
|
329
|
-
|
339
|
+
if defined?(Step) && Open.exists?(Step.info_file(obj.filename))
|
340
|
+
obj2str(Workflow.load_step(obj.filename))
|
341
|
+
else
|
342
|
+
"<IO:" << obj.filename << "--" << mtime_str(obj.filename) << ">"
|
343
|
+
end
|
330
344
|
else
|
331
345
|
|
332
346
|
if obj.respond_to? :obj2str
|
data/lib/rbbt/workflow.rb
CHANGED
@@ -385,7 +385,7 @@ module Workflow
|
|
385
385
|
next if default == v
|
386
386
|
next if (String === default and Symbol === v and v.to_s == default)
|
387
387
|
next if (Symbol === default and String === v and v == default.to_s)
|
388
|
-
real_inputs[k] = v
|
388
|
+
real_inputs[k.to_sym] = v
|
389
389
|
end
|
390
390
|
|
391
391
|
jobname_input_value = inputs[jobname_input] || all_defaults[jobname_input]
|
@@ -410,6 +410,7 @@ module Workflow
|
|
410
410
|
job.workflow = self
|
411
411
|
job.clean_name = jobname
|
412
412
|
job.overriden = overriden
|
413
|
+
job.real_inputs = real_inputs.keys
|
413
414
|
job
|
414
415
|
end
|
415
416
|
|
@@ -16,6 +16,10 @@ end
|
|
16
16
|
|
17
17
|
module Workflow
|
18
18
|
|
19
|
+
def self.job_path?(path)
|
20
|
+
path.split("/")[-4] == "jobs"
|
21
|
+
end
|
22
|
+
|
19
23
|
def log(status, message = nil, &block)
|
20
24
|
Step.log(status, message, nil, &block)
|
21
25
|
end
|
@@ -301,7 +305,9 @@ module Workflow
|
|
301
305
|
|
302
306
|
def setup_override_dependency(dep, workflow, task_name)
|
303
307
|
dep = Step === dep ? dep : Workflow.load_step(dep)
|
308
|
+
dep.workflow = workflow
|
304
309
|
dep.info[:name] = dep.name
|
310
|
+
dep.original_task_name ||= dep.task_name if dep.workflow
|
305
311
|
begin
|
306
312
|
workflow = Kernel.const_get workflow if String === workflow
|
307
313
|
dep.task = workflow.tasks[task_name] if dep.task.nil? && workflow.tasks.include?(task_name)
|
@@ -309,7 +315,7 @@ module Workflow
|
|
309
315
|
Log.exception $!
|
310
316
|
end
|
311
317
|
dep.task_name = task_name
|
312
|
-
dep.overriden =
|
318
|
+
dep.overriden = dep.original_task_name.to_sym
|
313
319
|
dep
|
314
320
|
end
|
315
321
|
|
@@ -360,7 +366,7 @@ module Workflow
|
|
360
366
|
compute = options[:compute]
|
361
367
|
|
362
368
|
options = IndiferentHash.setup(options.dup)
|
363
|
-
dep = dependency.call jobname,
|
369
|
+
dep = dependency.call jobname, _inputs.merge(options), real_dependencies
|
364
370
|
|
365
371
|
dep = [dep] unless Array === dep
|
366
372
|
|
@@ -77,6 +77,7 @@ module Workflow
|
|
77
77
|
task name do
|
78
78
|
raise RbbtException, "dependency not found in dep_task" if dependencies.empty?
|
79
79
|
dep = dependencies.last.join
|
80
|
+
raise dep.get_exception if dep.error?
|
80
81
|
set_info :result_type, dep.info[:result_type]
|
81
82
|
forget = config :forget_dep_tasks, :forget_dep_tasks, :default => FORGET_DEP_TASKS
|
82
83
|
if forget
|
@@ -50,8 +50,8 @@ module Workflow
|
|
50
50
|
case input_types[input]
|
51
51
|
when :file
|
52
52
|
Log.debug "Pointing #{ input } to #{file}"
|
53
|
-
if file =~ /\.
|
54
|
-
inputs[input.to_sym] = Open.read(file)
|
53
|
+
if file =~ /\.yaml/
|
54
|
+
inputs[input.to_sym] = YAML.load(Open.read(file))
|
55
55
|
else
|
56
56
|
inputs[input.to_sym] = Open.realpath(file)
|
57
57
|
end
|
data/lib/rbbt/workflow/step.rb
CHANGED
@@ -12,6 +12,9 @@ class Step
|
|
12
12
|
attr_accessor :exec
|
13
13
|
attr_accessor :relocated
|
14
14
|
attr_accessor :result, :mutex, :seen
|
15
|
+
attr_accessor :real_inputs, :original_task_name
|
16
|
+
|
17
|
+
RBBT_DEBUG_CLEAN = ENV["RBBT_DEBUG_CLEAN"] == 'true'
|
15
18
|
|
16
19
|
class << self
|
17
20
|
attr_accessor :lock_dir
|
@@ -143,11 +146,13 @@ class Step
|
|
143
146
|
seen = []
|
144
147
|
while path = deps.pop
|
145
148
|
dep_info = archived_info[path]
|
146
|
-
dep_info
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
149
|
+
if dep_info
|
150
|
+
dep_info[:inputs].each do |k,v|
|
151
|
+
all_inputs[k] = v unless all_inputs.include?(k)
|
152
|
+
end if dep_info[:inputs]
|
153
|
+
deps.concat(dep_info[:dependencies].collect{|p| p.last } - seen) if dep_info[:dependencies]
|
154
|
+
deps.concat(dep_info[:archived_dependencies].collect{|p| p.last } - seen) if dep_info[:archived_dependencies]
|
155
|
+
end
|
151
156
|
seen << path
|
152
157
|
end
|
153
158
|
|
@@ -156,7 +161,7 @@ class Step
|
|
156
161
|
|
157
162
|
def dependencies=(dependencies)
|
158
163
|
@dependencies = dependencies
|
159
|
-
set_info :dependencies, dependencies.collect{|dep| [dep.task_name, dep.name, dep.path]}
|
164
|
+
set_info :dependencies, dependencies.collect{|dep| [dep.task_name, dep.name, dep.path]} if dependencies
|
160
165
|
end
|
161
166
|
|
162
167
|
def recursive_inputs
|
@@ -454,6 +459,7 @@ class Step
|
|
454
459
|
status << "not running" if ! done? && ! running?
|
455
460
|
status.unshift " " if status.any?
|
456
461
|
Log.high "Cleaning step: #{path}#{status * " "}"
|
462
|
+
Log.stack caller if RBBT_DEBUG_CLEAN
|
457
463
|
abort if ! done? && running?
|
458
464
|
Step.clean(path)
|
459
465
|
self
|
@@ -8,6 +8,16 @@ class Step
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
+
def self.serialize_info(info)
|
12
|
+
info = info.clean_version if IndiferentHash === info
|
13
|
+
INFO_SERIALIZER.dump(info)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.load_serialized_info(io)
|
17
|
+
IndiferentHash.setup(INFO_SERIALIZER.load(io))
|
18
|
+
end
|
19
|
+
|
20
|
+
|
11
21
|
def self.wait_for_jobs(jobs)
|
12
22
|
jobs = [jobs] if Step === jobs
|
13
23
|
begin
|
@@ -59,7 +69,7 @@ class Step
|
|
59
69
|
def self.step_info(path)
|
60
70
|
begin
|
61
71
|
Open.open(info_file(path), :mode => 'rb') do |f|
|
62
|
-
|
72
|
+
self.load_serialized_info(f)
|
63
73
|
end
|
64
74
|
rescue Exception
|
65
75
|
Log.exception $!
|
@@ -83,18 +93,22 @@ class Step
|
|
83
93
|
|
84
94
|
Log.debug "Saving job input #{name} (#{type}) into #{path}"
|
85
95
|
case
|
96
|
+
when Step === value
|
97
|
+
Open.ln_s(value.path, path)
|
98
|
+
when type.to_s == "file"
|
99
|
+
if String === value && File.exists?(value)
|
100
|
+
Open.ln_s(value, path)
|
101
|
+
else
|
102
|
+
Open.write(path + '.yaml', value.to_yaml)
|
103
|
+
end
|
86
104
|
when Array === value
|
87
|
-
Open.write(path, value * "\n")
|
105
|
+
Open.write(path, value.collect{|v| Step === v ? v.path : v.to_s} * "\n")
|
88
106
|
when IO === value
|
89
|
-
|
90
|
-
|
91
|
-
if String === value && File.exists?(value)
|
92
|
-
Open.link(value, path)
|
107
|
+
if value.filename && String === value.filename && File.exists?(value.filename)
|
108
|
+
Open.ln_s(value.filename, path)
|
93
109
|
else
|
94
|
-
Open.write(path
|
110
|
+
Open.write(path, value)
|
95
111
|
end
|
96
|
-
when Step === value
|
97
|
-
value = value.produce.load
|
98
112
|
else
|
99
113
|
Open.write(path, value.to_s)
|
100
114
|
end
|
@@ -104,18 +118,24 @@ class Step
|
|
104
118
|
def self.save_job_inputs(job, dir, options = nil)
|
105
119
|
options = IndiferentHash.setup options.dup if options
|
106
120
|
|
107
|
-
task_name = job.task_name
|
121
|
+
task_name = Symbol === job.overriden ? job.overriden : job.task_name
|
108
122
|
workflow = job.workflow
|
109
123
|
workflow = Kernel.const_get workflow if String === workflow
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
124
|
+
if workflow
|
125
|
+
task_info = workflow.task_info(task_name)
|
126
|
+
input_types = task_info[:input_types]
|
127
|
+
task_inputs = task_info[:inputs]
|
128
|
+
input_defaults = task_info[:input_defaults]
|
129
|
+
else
|
130
|
+
task_info = input_types = task_inputs = input_defaults = {}
|
131
|
+
end
|
114
132
|
|
115
133
|
inputs = {}
|
134
|
+
real_inputs = job.real_inputs || job.info[:real_inputs]
|
116
135
|
job.recursive_inputs.zip(job.recursive_inputs.fields).each do |value,name|
|
117
136
|
next unless task_inputs.include? name.to_sym
|
118
|
-
next
|
137
|
+
next unless real_inputs.include? name.to_sym
|
138
|
+
next if options && ! options.include?(name)
|
119
139
|
next if value.nil?
|
120
140
|
next if input_defaults[name] == value
|
121
141
|
inputs[name] = value
|
@@ -188,7 +208,7 @@ class Step
|
|
188
208
|
info_lock.lock if check_lock and false
|
189
209
|
begin
|
190
210
|
Open.open(info_file, :mode => 'rb') do |file|
|
191
|
-
|
211
|
+
Step.load_serialized_info(file)
|
192
212
|
end
|
193
213
|
ensure
|
194
214
|
info_lock.unlock if check_lock and false
|
@@ -204,7 +224,7 @@ class Step
|
|
204
224
|
Log.debug{"Error loading info file: " + info_file}
|
205
225
|
Log.exception $!
|
206
226
|
Open.rm info_file
|
207
|
-
Misc.sensiblewrite(info_file,
|
227
|
+
Misc.sensiblewrite(info_file, Step.serialize_info({:status => :error, :messages => ["Info file lost"]}))
|
208
228
|
raise $!
|
209
229
|
end
|
210
230
|
end
|
@@ -212,10 +232,10 @@ class Step
|
|
212
232
|
def init_info(force = false)
|
213
233
|
return nil if @exec || info_file.nil? || (Open.exists?(info_file) && ! force)
|
214
234
|
Open.lock(info_file, :lock => info_lock) do
|
215
|
-
i = {:status => :waiting, :pid => Process.pid, :path => path}
|
235
|
+
i = {:status => :waiting, :pid => Process.pid, :path => path, :real_inputs => real_inputs}
|
216
236
|
i[:dependencies] = dependencies.collect{|dep| [dep.task_name, dep.name, dep.path]} if dependencies
|
217
|
-
|
218
|
-
|
237
|
+
Misc.sensiblewrite(info_file, Step.serialize_info(i), :force => true, :lock => false)
|
238
|
+
@info_cache = IndiferentHash.setup(i)
|
219
239
|
@info_cache_time = Time.now
|
220
240
|
end
|
221
241
|
end
|
@@ -227,9 +247,9 @@ class Step
|
|
227
247
|
Open.lock(info_file, :lock => info_lock) do
|
228
248
|
i = info(false).dup
|
229
249
|
i[key] = value
|
230
|
-
|
231
|
-
|
232
|
-
Misc.sensiblewrite(info_file, dump, :force => true, :lock => false)
|
250
|
+
dump = Step.serialize_info(i)
|
251
|
+
@info_cache = IndiferentHash.setup(i)
|
252
|
+
Misc.sensiblewrite(info_file, dump, :force => true, :lock => false) if Open.exists?(info_file)
|
233
253
|
@info_cache_time = Time.now
|
234
254
|
value
|
235
255
|
end
|
@@ -242,9 +262,9 @@ class Step
|
|
242
262
|
Open.lock(info_file, :lock => info_lock) do
|
243
263
|
i = info(false)
|
244
264
|
i.merge! hash
|
245
|
-
|
246
|
-
|
247
|
-
Misc.sensiblewrite(info_file, dump, :force => true, :lock => false)
|
265
|
+
dump = Step.serialize_info(i)
|
266
|
+
@info_cache = IndiferentHash.setup(i)
|
267
|
+
Misc.sensiblewrite(info_file, dump, :force => true, :lock => false) if Open.exists?(info_file)
|
248
268
|
@info_cache_time = Time.now
|
249
269
|
value
|
250
270
|
end
|
@@ -537,7 +557,7 @@ class Step
|
|
537
557
|
end
|
538
558
|
|
539
559
|
def file(name)
|
540
|
-
Path.setup(File.join(files_dir, name.to_s))
|
560
|
+
Path.setup(File.join(files_dir, name.to_s), workflow, self)
|
541
561
|
end
|
542
562
|
|
543
563
|
def save_file(name, content)
|