scout-gear 9.0.0 → 9.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 98b3f84350f2f451b717f004bfc97cd7767f7e0fed58e1a2d0cd023bc700185f
4
- data.tar.gz: '00847ad4d92b1cab22305ea867dc12c870b22e192e79536917f82aa90cc82989'
3
+ metadata.gz: bbe916f2d82030e26b1049319216f45341b7e7f7410df65e3953c0a733af2c21
4
+ data.tar.gz: 152b31b552f90b2bb06aed9bc3cdd12a3bff48746468e52b39e5754977a40f90
5
5
  SHA512:
6
- metadata.gz: 4af5e13873fa13dea33161e9200aa725bb4329899e9242ae6504ffae2ab82e7eb01b82a53edc2a4331ede06d0f12bb07f86f579cb29ecbf0d36e791bf6d53f3e
7
- data.tar.gz: fc773eb933e5c851aa1d642e872df02d282fbcb29ee1b8e1bf150ffebf9bd4ce8c3670d43a284ff2c8197477a8cf6f71dfcd49b265ad938c723292ec57579998
6
+ metadata.gz: 51f41961b2a95d847e4a60a70a2774c34098c4fa502818be8b9d5ec30ced53fce81d02862a1104498060c04a88138ca329ae82ba09fa264b6d9ec0daca80a3a4
7
+ data.tar.gz: 113d51ff9798416073a86debb07ce47708254a4594e5785cfc4543794c3ff4e09da8809c29d67f64d57aeacd30c1c2ad49998d5eca4f544f3381991c296db1c5
data/VERSION CHANGED
@@ -1 +1 @@
1
- 9.0.0
1
+ 9.1.0
data/bin/scout CHANGED
@@ -99,7 +99,6 @@ if config_keys = options.delete(:config_keys)
99
99
  end
100
100
  end
101
101
 
102
-
103
102
  $scout_command_dir = Scout.bin.scout
104
103
  $scout_command_dir.path_maps[:scout_commands] = File.join(File.dirname(__dir__), "{PATH/bin\\/scout/scout_commands}")
105
104
 
data/lib/rbbt-scout.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  $LOAD_PATH.unshift File.join(__dir__, '../modules/rbbt-util/lib')
2
- module Rbbt
2
+ module Scout
3
3
  extend Resource
4
4
  self.path_maps = Path.path_maps.merge(:rbbt_lib => File.expand_path(File.join(__dir__, '../modules/rbbt-util/', '{TOPLEVEL}','{SUBPATH}')))
5
5
  end
6
+ Rbbt = Scout
6
7
 
7
8
  Resource.set_software_env Rbbt.software
data/lib/scout/config.rb CHANGED
@@ -4,7 +4,7 @@ require_relative 'resource/scout'
4
4
 
5
5
  module Scout::Config
6
6
 
7
- CACHE = IndiferentHash.setup({})
7
+ CACHE ||= IndiferentHash.setup({})
8
8
 
9
9
  GOT_KEYS=[]
10
10
 
@@ -6,7 +6,7 @@ module IndiferentHash
6
6
  defaults = string2hash defaults if String === defaults
7
7
 
8
8
  defaults.each do |key, value|
9
- next if options.include? key
9
+ next if options.include?(key)
10
10
 
11
11
  options[key] = value
12
12
  end
@@ -29,7 +29,7 @@ module Misc
29
29
  obj.to_s
30
30
  when Float
31
31
  if obj % 1 == 0
32
- obj.to_i
32
+ obj.to_i.to_s
33
33
  elsif obj.abs > 10
34
34
  "%.1f" % obj
35
35
  elsif obj.abs > 1
@@ -86,6 +86,26 @@ module Path
86
86
  self.annotate(self.split(".")[0..-2] * ".")
87
87
  end
88
88
 
89
+ def remove_extension(extension = nil)
90
+ if extension.nil?
91
+ unset_extension
92
+ else
93
+ self.annotate(self.sub(/\.#{extension}$/,''))
94
+ end
95
+ end
96
+
97
+ def replace_extension(new_extension = nil, multiple = false)
98
+ if String === multiple
99
+ new_path = self.sub(/(\.[^\.\/]{1,5})(.#{multiple})?$/,'')
100
+ elsif multiple
101
+ new_path = self.sub(/(\.[^\.\/]{1,5})+$/,'')
102
+ else
103
+ new_path = self.sub(/\.[^\.\/]{1,5}$/,'')
104
+ end
105
+ new_path = new_path + "." + new_extension.to_s
106
+ self.annotate(new_path)
107
+ end
108
+
89
109
 
90
110
  # Is 'file' newer than 'path'? return non-true if path is newer than file
91
111
  def self.newer?(path, file, by_link = false)
@@ -28,7 +28,11 @@ module Resource
28
28
  m.named_captures.include?(c) ? m[c] : nil
29
29
  }.compact * "/"
30
30
  unlocated.gsub!(/\/+/,'/')
31
- unlocated[self.subdir] = "" if self.subdir
31
+ if self.subdir && ! self.subdir.empty?
32
+ subdir = self.subdir
33
+ subdir += "/" unless subdir.end_with?("/")
34
+ unlocated[subdir] = ""
35
+ end
32
36
  choices << self.annotate(unlocated)
33
37
  end
34
38
  end
@@ -19,6 +19,7 @@ module TSV
19
19
  preamble_str = nil
20
20
  end
21
21
 
22
+ preamble_str = preamble_str.strip if preamble_str
22
23
  [preamble_str, fields_str].compact * "\n"
23
24
  end
24
25
 
@@ -20,7 +20,7 @@ module Open
20
20
  into.add *res
21
21
  when TSV, Hash
22
22
  key, value = res
23
- if into.type == :double
23
+ if TSV === into && into.type == :double
24
24
  into.zip_new key, value, insitu: false
25
25
  else
26
26
  into[key] = value
@@ -118,6 +118,12 @@ module Open
118
118
  callback.call res if callback
119
119
  nil
120
120
  end
121
+ when Hash
122
+ obj.each do |key,value|
123
+ res = block.call(key,value)
124
+ callback.call res if callback
125
+ nil
126
+ end
121
127
  when Array
122
128
  obj.each do |line|
123
129
  res = block.call(line)
@@ -122,7 +122,7 @@ Example:
122
122
  end
123
123
 
124
124
  def digest_str
125
- fingerprint
125
+ "TSV:{"<< Log.fingerprint(self.all_fields|| []) << ";" << Log.fingerprint(self.keys) << ";" << Log.fingerprint(self.values) << "}"
126
126
  end
127
127
 
128
128
  def inspect
@@ -7,11 +7,12 @@ class Step
7
7
  direct_deps << dep
8
8
  end
9
9
  seen.concat direct_deps.collect{|d| d.path }
10
- direct_deps.inject(direct_deps){|acc,d| acc.concat(d.rec_dependencies(connected, [])); acc }
10
+ direct_deps.inject(direct_deps){|acc,d| acc.concat(d.rec_dependencies(connected, seen)); acc }
11
11
  end
12
12
 
13
13
  def recursive_inputs
14
- recursive_inputs = @inputs.to_hash
14
+ recursive_inputs = NamedArray === @inputs ? @inputs.to_hash : {}
15
+ return recursive_inputs if dependencies.nil?
15
16
  dependencies.inject(recursive_inputs) do |acc,dep|
16
17
  acc.merge(dep.recursive_inputs)
17
18
  end
@@ -2,7 +2,11 @@ class Step
2
2
  def files_dir
3
3
  @files_dir ||= begin
4
4
  dir = @path + ".files"
5
- @path.annotate(dir) if Path === @path
5
+ if Path === @path
6
+ @path.annotate(dir)
7
+ else
8
+ Path.setup(dir)
9
+ end
6
10
  dir.pkgdir = self
7
11
  dir
8
12
  end
@@ -9,12 +9,18 @@ class Step
9
9
  end
10
10
  end
11
11
 
12
+ def self.load_info(info_file)
13
+ info = Persist.load(info_file, SERIALIZER) || {}
14
+ IndiferentHash.setup(info)
15
+ end
16
+
12
17
  def load_info
13
- @info = Persist.load(info_file, SERIALIZER) || {}
14
- IndiferentHash.setup(@info)
18
+ @info = Step.load_info(info_file)
15
19
  @info_load_time = Time.now
16
20
  end
17
21
 
22
+
23
+
18
24
  def save_info(info = nil)
19
25
  Persist.save(info, info_file, SERIALIZER)
20
26
  @info_load_time = Time.now
@@ -83,10 +83,25 @@ class Step
83
83
  end
84
84
  end
85
85
 
86
- str << "\n"
86
+ str
87
87
  end
88
88
 
89
- def self.prov_report(step, offset = 0, task = nil, seen = [], expand_repeats = false, input = nil)
89
+ def self.prov_indent(step, offset, input_dependencies)
90
+ return (" " * (offset))
91
+ if step.alias?
92
+ (" " * offset + "🡵")
93
+ elsif step.overriden_task
94
+ (" " * offset + "🡇")
95
+ elsif input_dependencies.include?(step)
96
+ (" " * offset + "┝")
97
+ elsif step.input_dependencies.any?
98
+ (" " * offset + "╰")
99
+ else
100
+ (" " * (offset+1))
101
+ end
102
+ end
103
+
104
+ def self.prov_report(step, offset = 0, task = nil, seen = [], expand_repeats = false, input = nil, input_dependencies = nil)
90
105
  info = step.info || {}
91
106
  info[:task_name] = task
92
107
  path = step.path
@@ -101,7 +116,7 @@ class Step
101
116
 
102
117
  this_step_msg = prov_report_msg(status, name, path, info, input)
103
118
 
104
- input_dependencies = {}
119
+ input_dependencies ||= {}
105
120
  step.dependencies.each do |dep|
106
121
  if dep.input_dependencies.any?
107
122
  dep.input_dependencies.each do |id|
@@ -116,8 +131,10 @@ class Step
116
131
  end
117
132
  end if step.dependencies
118
133
 
119
- str = ""
120
- str = " " * offset + this_step_msg if ENV["SCOUT_ORIGINAL_STACK"] == 'true'
134
+ str = []
135
+
136
+ indent = prov_indent(step, offset, input_dependencies)
137
+ str << indent + this_step_msg if ENV["SCOUT_ORIGINAL_STACK"] == 'true'
121
138
 
122
139
  step.dependencies.dup.tap{|l|
123
140
  l.reverse! if ENV["SCOUT_ORIGINAL_STACK"] == 'true'
@@ -126,10 +143,10 @@ class Step
126
143
  new = ! seen.include?(path)
127
144
  if new
128
145
  seen << path
129
- str << prov_report(dep, offset + 1, task, seen, expand_repeats, input_dependencies[dep])
146
+ str.concat(prov_report(dep, offset + 1, task, seen, expand_repeats, input_dependencies[dep], input_dependencies.dup).split("\n"))
130
147
  else
131
148
  if expand_repeats
132
- str << Log.color(Step.status_color(dep.status), Log.uncolor(prov_report(dep, offset+1, task)))
149
+ str << Log.color(Step.status_color(dep.status), Log.uncolor(prov_report(dep, offset+1, task, input_dependencies[dep], input_dependencies.dup)))
133
150
  else
134
151
  info = dep.info || {}
135
152
  status = info[:status] || :missing
@@ -138,13 +155,14 @@ class Step
138
155
  status = :unsync if status == :done and not Open.exist?(path)
139
156
  status = :notfound if status == :noinfo and not Open.exist?(path)
140
157
 
141
- str << Log.color(Step.status_color(status), " " * (offset + 1) + Log.uncolor(prov_report_msg(status, name, path, info, input_dependencies[dep])))
158
+ dep_indent = prov_indent(dep, offset+1, input_dependencies)
159
+ str << dep_indent + Log.color(Step.status_color(status), Log.uncolor(prov_report_msg(status, name, path, info, input_dependencies[dep])))
142
160
  end
143
161
  end
144
162
  end if step.dependencies
145
163
 
146
- str += (" " * offset) + this_step_msg unless ENV["SCOUT_ORIGINAL_STACK"] == 'true'
164
+ str << indent + this_step_msg unless ENV["SCOUT_ORIGINAL_STACK"] == 'true'
147
165
 
148
- str
166
+ str * "\n"
149
167
  end
150
168
  end
@@ -33,6 +33,10 @@ class Step
33
33
  Open.rm_rf files_dir if Open.exist?(files_dir)
34
34
  end
35
35
 
36
+ def self.clean(file)
37
+ Step.new(file).clean
38
+ end
39
+
36
40
 
37
41
  def recursive_clean
38
42
  dependencies.each do |dep|
@@ -33,7 +33,9 @@ class Step
33
33
  def inputs
34
34
  @inputs ||= begin
35
35
  if info_file && Open.exists?(info_file)
36
- info[:inputs]
36
+ inputs = info[:inputs]
37
+ NamedArray.setup(inputs, info[:input_names]) if inputs && info[:input_names]
38
+ inputs
37
39
  else
38
40
  []
39
41
  end
@@ -61,6 +63,10 @@ class Step
61
63
  @name ||= File.basename(@path)
62
64
  end
63
65
 
66
+ def short_path
67
+ [workflow.to_s, task_name, name] * "/"
68
+ end
69
+
64
70
  def clean_name
65
71
  return @id if @id
66
72
  return info[:clean_name] if info.include? :clean_name
@@ -122,9 +128,10 @@ class Step
122
128
  begin
123
129
  Persist.persist(name, type, :path => path, :tee_copies => tee_copies) do
124
130
  clear_info
131
+ input_names = (task.respond_to?(:inputs) && task.inputs) ? task.inputs.collect{|name,_| name} : []
125
132
  merge_info :status => :start, :start => Time.now,
126
133
  :pid => Process.pid, :pid_hostname => Misc.hostname,
127
- :inputs => MetaExtension.purge(inputs), :type => type,
134
+ :inputs => MetaExtension.purge(inputs), :input_names => input_names, :type => type,
128
135
  :dependencies => dependencies.collect{|d| d.path }
129
136
 
130
137
  @result = exec
@@ -278,9 +285,10 @@ class Step
278
285
  end
279
286
 
280
287
  def step(task_name)
288
+ task_name = task_name.to_sym
281
289
  dependencies.each do |dep|
282
- return dep if dep.task_name == task_name
283
- return dep if dep.overriden_task == task_name
290
+ return dep if dep.task_name && dep.task_name.to_sym == task_name
291
+ return dep if dep.overriden_task && dep.overriden_task.to_sym == task_name
284
292
  rec_dep = dep.step(task_name)
285
293
  return rec_dep if rec_dep
286
294
  end
@@ -302,4 +310,8 @@ class Step
302
310
  def task_signature
303
311
  [workflow.to_s, task_name] * "#"
304
312
  end
313
+
314
+ def alias?
315
+ task.alias? if task
316
+ end
305
317
  end
@@ -54,6 +54,7 @@ module Task
54
54
  if provided_inputs.include?(overriden = [workflow.name, task] * "#")
55
55
  dep = provided_inputs[overriden]
56
56
  dep = Step.new dep unless Step === dep
57
+ dep = dep.dup
57
58
  dep.type = workflow.tasks[task].type
58
59
  dep.overriden_task = task
59
60
  dep.overriden_workflow = workflow
@@ -87,6 +87,8 @@ module Task
87
87
  if Path.is_filename?(value)
88
88
  relative_file = save_file_input(value, directory)
89
89
  Open.write(input_file + ".as_file", relative_file)
90
+ elsif Step === value
91
+ Open.write(input_file + ".as_step", value.short_path)
90
92
  elsif type == :file
91
93
  relative_file = save_file_input(value, directory)
92
94
  Persist.save(relative_file, input_file, :file)
@@ -115,6 +117,9 @@ module Task
115
117
  value = Open.read(filename).strip
116
118
  value.sub!(/^\./, File.dirname(filename)) if value.start_with?("./")
117
119
  inputs[name] = value
120
+ elsif filename.end_with?('.as_step')
121
+ value = Open.read(filename).strip
122
+ inputs[name] = Step.load value
118
123
  elsif (options && (options[:noload] || options[:stream] || options[:nofile]))
119
124
  inputs[name] = filename
120
125
  else
@@ -45,6 +45,21 @@ module Task
45
45
  IndiferentHash.setup(provided_inputs)
46
46
  id = DEFAULT_NAME if id.nil?
47
47
 
48
+
49
+ missing_inputs = []
50
+ self.inputs.each do |input,type,desc,val,options|
51
+ next unless options && options[:required]
52
+ missing_inputs << input unless provided_inputs.include?(input)
53
+ end if self.inputs
54
+
55
+ if missing_inputs.length == 1
56
+ raise ParameterException, "Input '#{missing_inputs.first}' is required but was not provided or is nil"
57
+ end
58
+
59
+ if missing_inputs.length > 1
60
+ raise ParameterException, "Inputs #{Misc.humanize_list(missing_inputs)} are required but were not provided or are nil"
61
+ end
62
+
48
63
  provided_inputs = load_inputs(provided_inputs[:load_inputs]) if Hash === provided_inputs && provided_inputs[:load_inputs]
49
64
 
50
65
  inputs, non_default_inputs, input_digest_str = process_inputs provided_inputs, id
@@ -52,7 +67,7 @@ module Task
52
67
  compute = {}
53
68
  dependencies = dependencies(id, provided_inputs, non_default_inputs, compute)
54
69
 
55
- non_default_inputs.concat provided_inputs.keys.select{|k| String === k && k.include?("#") } if Hash === provided_inputs
70
+ #non_default_inputs.concat provided_inputs.keys.select{|k| String === k && k.include?("#") } if Hash === provided_inputs
56
71
 
57
72
  non_default_inputs.uniq!
58
73
 
@@ -95,4 +110,8 @@ module Task
95
110
  Step.new path.find, inputs, dependencies, id, non_default_inputs, step_provided_inputs, compute, &self
96
111
  end
97
112
  end
113
+
114
+ def alias?
115
+ @extension == :dep_task
116
+ end
98
117
  end
@@ -136,6 +136,7 @@ module Workflow
136
136
 
137
137
  dep_tree = {}
138
138
  task = self.tasks[task_name]
139
+ raise "TaskNotFound: #{task_name}" if task.nil?
139
140
  task.deps.each do |workflow, task, options|
140
141
  next if seen.include? dep
141
142
  seen << [workflow, task, options.merge(seen_options)]
@@ -27,7 +27,9 @@ module Workflow
27
27
 
28
28
  def self.require_workflow(workflow_name_orig)
29
29
  first = nil
30
- workflow_name_orig.split("+").each do |workflow_name|
30
+ workflow_name_orig.split("+").each do |complete_workflow_name|
31
+ self.main = nil
32
+ workflow_name, *subworkflows = complete_workflow_name.split("::")
31
33
  workflow = workflow_name
32
34
  workflow = Path.setup('workflows')[workflow_name]["workflow.rb"] unless Open.exists?(workflow)
33
35
  workflow = Path.setup('workflows')[Misc.snake_case(workflow_name)]["workflow.rb"] unless Open.exists?(workflow)
@@ -40,7 +42,14 @@ module Workflow
40
42
  else
41
43
  raise "Workflow #{workflow_name} not found"
42
44
  end
43
- first ||= self.main || workflows.last
45
+
46
+ current = begin
47
+ Kernel.const_get(complete_workflow_name)
48
+ rescue
49
+ self.main || workflows.last
50
+ end
51
+
52
+ first ||= current
44
53
  end
45
54
  first
46
55
  end
data/scout-gear.gemspec CHANGED
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: scout-gear 9.0.0 ruby lib
5
+ # stub: scout-gear 9.1.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "scout-gear".freeze
9
- s.version = "9.0.0"
9
+ s.version = "9.1.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Miguel Vazquez".freeze]
14
- s.date = "2023-06-27"
14
+ s.date = "2023-06-30"
15
15
  s.description = "Temporary files, logs, path, resources, persistence, workflows, TSV, etc.".freeze
16
16
  s.email = "mikisvaz@gmail.com".freeze
17
17
  s.executables = ["scout".freeze]
@@ -18,6 +18,7 @@ $ #{$0} [<options>] <workflow> <task>
18
18
  -h--help Print this help
19
19
  --nostream Disable job streaming
20
20
  --update Update jobs with newer dependencies
21
+ --deploy* Deploy mode: serial, local, or SLURM (default 'serial')
21
22
  -jn--jobname* Name to use as job identifier
22
23
  -li--load_inputs* Directory with inputs files to load
23
24
  -pf--print_filepath Print the file path
@@ -25,7 +26,6 @@ $ #{$0} [<options>] <workflow> <task>
25
26
  -cl--clean Clean the last step
26
27
  -rcl--recursive_clean Clean all steps
27
28
  -ct--clean_task* Clean a particular task
28
- -d--deploy* Deploy mode: serial, local, or SLURM (default 'serial')
29
29
  -od--override_deps* Override deps using 'Workflow#task=<path>' array_separated
30
30
  EOF
31
31
 
@@ -86,8 +86,7 @@ if clean_task
86
86
  dep.set_info :status, :cleaned
87
87
  end
88
88
 
89
- job.clean if job.task_name.to_s == clean_task.to_s
90
- job.clean unless job.updated?
89
+ job.clean if (job.task_name.to_s == clean_task.to_s) || ! job.updated?
91
90
  end
92
91
  end
93
92
 
@@ -74,4 +74,19 @@ end
74
74
  assert_equal list, file.list
75
75
  end
76
76
  end
77
+
78
+ def test_produce_compressed
79
+ dir = tmpdir.directory[__method__]
80
+ dir.pkgdir = Scout
81
+ list = %w(a b)
82
+
83
+ Scout.claim dir["foo.gz"], :proc do |filename|
84
+ list
85
+ end
86
+
87
+ Misc.in_dir(dir) do
88
+ file = dir.foo
89
+ assert_include file.produce.find, '.gz'
90
+ end
91
+ end
77
92
  end
@@ -7,6 +7,15 @@ class TestResourceUtil < Test::Unit::TestCase
7
7
  assert_equal 'share/data/somedir/somepath', Scout.identify(path)
8
8
  end
9
9
 
10
+ def test_identify_with_subdir
11
+ m = Module.new
12
+ m.extend Resource
13
+ m.subdir = 'test/subdir'
14
+ path = m.root['share/data/somedir/somepath'].find
15
+ assert_equal 'share/data/somedir/somepath', m.identify(path)
16
+ end
17
+
18
+
10
19
  def test_relocate
11
20
  TmpFile.with_file do |dir|
12
21
  Path.setup dir
@@ -3,7 +3,7 @@ require File.expand_path(__FILE__).sub(%r(.*/test/), '').sub(/test_(.*)\.rb/,'\1
3
3
 
4
4
  require 'scout/workflow'
5
5
  class TestStepProvenance < Test::Unit::TestCase
6
- def test_true
6
+ def test_prov_report
7
7
  tmpfile = tmpdir.test_step
8
8
  step1 = Step.new tmpfile.step1, ["12"] do |s|
9
9
  s.length
@@ -21,5 +21,28 @@ class TestStepProvenance < Test::Unit::TestCase
21
21
  assert_include Step.prov_report(step2), 'step1'
22
22
  assert_include Step.prov_report(step2), 'step2'
23
23
  end
24
+
25
+ def __test_input_dependencies
26
+ wf = Workflow.annonymous_workflow "TaskInputDepProv" do
27
+ input :value1, :integer
28
+ task :number1 => :integer do |i| i end
29
+
30
+ input :value2, :integer
31
+ task :number2 => :integer do |i| i end
32
+
33
+ input :number1, :integer
34
+ input :number2, :integer
35
+ task :sum => :integer do |i1,i2| i1 + i2 end
36
+
37
+ dep :number1, :value1 => 1
38
+ dep :number2, :value1 => 2
39
+ dep_task :my_sum, self, :sum, :number1 => :number1, :number2 => :number2
40
+
41
+ end
42
+ job = wf.job(:my_sum)
43
+
44
+ ppp Step.prov_report(job)
45
+
46
+ end
24
47
  end
25
48
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scout-gear
3
3
  version: !ruby/object:Gem::Version
4
- version: 9.0.0
4
+ version: 9.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miguel Vazquez
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-27 00:00:00.000000000 Z
11
+ date: 2023-06-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: term-ansicolor