scout-gear 10.7.13 → 10.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: da828c9bca6f9c8fd81012609f680393fb3ee4a64858927c49bb6c67e7919365
4
- data.tar.gz: 804033931a4f77c5f5ee592c139fa00bbe0ae71b1622f945b01c7fb979e4bafe
3
+ metadata.gz: a16e6fadbdab77f5dd56efff644adb5b79c7765642d87cee21f297eb4e783bee
4
+ data.tar.gz: 2d4e225ec569e3fc8f569835b865effc6b3428e23b6feba3d4f9e8d092cb8a13
5
5
  SHA512:
6
- metadata.gz: 97403960a81eaca702c6c0eb62efacc8810cb0fa2005492b708834917e9665cdb3b9d19b68a8bdfefe9a26070f87d8ed35052642f7a7d2565750be6b0a677e4e
7
- data.tar.gz: 4fbc6798b0775464f4b10084b74064cefc5accac797a64d09c84485eecb92b8dc09367f8a25589274ceb0fefffd769c9c0ed9e6ed7f303f45426c4115033882b
6
+ metadata.gz: 3103bc1c224105e7a5d5ac74181a62c6457d6b38cb4f59db2615be3962cef24777f25918977d738c02903b0b831606991c1015b2a7fa77fb03def9fce1b29cc7
7
+ data.tar.gz: a09e7bf564fc5e4711bee14df56b7e8cd108b52cd9f02318d255964b58a9bc7585829a411629733c9cdd78060ac8d9b932491924d8227c03540fe93f55d04ccf
data/VERSION CHANGED
@@ -1 +1 @@
1
- 10.7.13
1
+ 10.8.1
data/bin/scout CHANGED
@@ -66,7 +66,7 @@ require 'scout/simple_opt'
66
66
  require 'scout/log'
67
67
 
68
68
  options = SOPT.setup <<EOF
69
- Ruby bioinformatics toolkit
69
+ Scout
70
70
 
71
71
  $ #{$0} <command> <subcommand> ... -a --arg1 --arg2='value' --arg3 'another-value'
72
72
 
@@ -75,7 +75,7 @@ $ #{$0} <command> <subcommand> ... -a --arg1 --arg2='value' --arg3 'another-valu
75
75
  --dev* #{Log.color :yellow, "Find development libraries in the directory specified"}
76
76
  --nocolor #{Log.color :yellow, "Disable colored output"}
77
77
  --nobar #{Log.color :yellow, "Disable progress report"}
78
- --locate_file #{Log.color :yellow, "Report the location of the script instead of executing it"}
78
+ --locate_file #{Log.color :yellow, "Report the location of the script but do not execute it"}
79
79
  -ck--config_keys* #{Log.color :yellow, "Override some config keys"}
80
80
  EOF
81
81
 
@@ -69,7 +69,7 @@ module Association
69
69
  end
70
70
  end
71
71
 
72
- tsv = transformer.tsv **kwargs.merge(data: data, fields: fields)
72
+ tsv = transformer.tsv **kwargs.merge(data: data, fields: fields).except(:identifiers)
73
73
  end
74
74
  index.extend Index
75
75
  index.parse_key_field
@@ -106,8 +106,19 @@ module Association
106
106
 
107
107
  transformer.traverse key_field: original_source_header, fields: all_fields.values_at(*field_pos) do |k,v|
108
108
  v = v.dup if TSV === obj
109
- k = source_index[k] if source_index
110
- v[0] = Array === v[0] ? target_index.values_at(*v[0]) : target_index[v[0]] if target_index
109
+ if source_index
110
+ k = source_index[k]
111
+ next if k.nil? or k.empty?
112
+ end
113
+ if target_index
114
+ if Array === v[0]
115
+ v[0] = target_index.values_at(*v[0])
116
+ v = v.reject{|l| l[0].nil? || l[0].empty?}
117
+ else
118
+ v[0] = target_index[v[0]]
119
+ next if v[0].nil? or v[0].empty?
120
+ end
121
+ end
111
122
  [k, v]
112
123
  end
113
124
 
@@ -78,6 +78,13 @@ class KnowledgeBase
78
78
  else
79
79
  identifier_files = database_identifier_files(name)
80
80
  end
81
+ if registered_identifiers = registered_options(name)[:identifiers]
82
+ if Array === registered_identifiers
83
+ identifier_files.concat registered_identifiers
84
+ else
85
+ identifier_files.push registered_identifiers
86
+ end
87
+ end
81
88
  identifier_files.concat Entity.identifier_files(source(name)) if defined? Entity
82
89
  identifier_files.uniq!
83
90
  identifier_files.collect!{|f| (Path === f) ? f : Path.setup(f.dup) }
@@ -95,6 +102,13 @@ class KnowledgeBase
95
102
  else
96
103
  identifier_files = database_identifier_files(name)
97
104
  end
105
+ if registered_identifiers = registered_options(name)[:identifiers]
106
+ if Array === registered_identifiers
107
+ identifier_files.concat registered_identifiers
108
+ else
109
+ identifier_files.push registered_identifiers
110
+ end
111
+ end
98
112
  identifier_files.concat Entity.identifier_files(target(name)) if defined? Entity
99
113
  identifier_files.uniq!
100
114
  identifier_files.collect!{|f| f.annotate(f.gsub(/\bNAMESPACE\b/, namespace))} if self.namespace
@@ -106,7 +106,7 @@ class KnowledgeBase
106
106
  new_paths = _fp(rest, clean_matches, new_assignments)
107
107
  next unless new_paths
108
108
  paths[match] = new_paths
109
- end
109
+ end if matches
110
110
 
111
111
  return false if paths.empty?
112
112
 
@@ -108,43 +108,43 @@ if continue
108
108
  end
109
109
  end
110
110
 
111
- def write_and_close
112
- begin
113
- write
114
- yield
115
- ensure
116
- close
111
+ def write_and_close
112
+ begin
113
+ write
114
+ yield
115
+ ensure
116
+ close
117
+ end
117
118
  end
118
- end
119
119
 
120
- def self.importtsv(database, stream)
121
- begin
122
- bin = case database
123
- when TokyoCabinet::HDB
124
- 'tchmgr'
125
- when TokyoCabinet::BDB
126
- 'tcbmgr'
127
- else
128
- raise "Database not HDB or BDB: #{Log.fingerprint database}"
129
- end
130
-
131
- database.close
132
- CMD.cmd("#{bin} version", :log => false)
133
- FileUtils.mkdir_p File.dirname(database.persistence_path)
134
- CMD.cmd("#{bin} importtsv '#{database.persistence_path}'", :in => stream, :log => false, :dont_close_in => true)
135
- rescue
136
- Log.debug("tchmgr importtsv failed for: #{database.persistence_path}")
120
+ def self.importtsv(database, stream)
121
+ begin
122
+ bin = case database
123
+ when TokyoCabinet::HDB
124
+ 'tchmgr'
125
+ when TokyoCabinet::BDB
126
+ 'tcbmgr'
127
+ else
128
+ raise "Database not HDB or BDB: #{Log.fingerprint database}"
129
+ end
130
+
131
+ database.close
132
+ CMD.cmd("#{bin} version", :log => false)
133
+ FileUtils.mkdir_p File.dirname(database.persistence_path)
134
+ CMD.cmd("#{bin} importtsv '#{database.persistence_path}'", :in => stream, :log => false, :dont_close_in => true)
135
+ rescue
136
+ Log.debug("tchmgr importtsv failed for: #{database.persistence_path}")
137
+ end
137
138
  end
138
- end
139
139
 
140
- class << self
141
- alias load_stream importtsv
142
- end
140
+ class << self
141
+ alias load_stream importtsv
142
+ end
143
143
 
144
- def importtsv(stream)
145
- ScoutCabinet.load_stream(self, stream)
146
- end
144
+ def importtsv(stream)
145
+ ScoutCabinet.load_stream(self, stream)
146
+ end
147
147
 
148
- alias load_stream importtsv
148
+ alias load_stream importtsv
149
149
  end
150
150
  end
@@ -37,6 +37,7 @@ module TSVAdapter
37
37
  else
38
38
  begin
39
39
  TSV.setup(base, base.load_annotation_hash)
40
+ base.filename = base.persistence_path if base.filename.nil?
40
41
  rescue
41
42
  TSV.setup(base)
42
43
  base.save_annotation_hash
@@ -56,6 +56,7 @@ module TSV
56
56
 
57
57
  files.each do |file|
58
58
  #next if Path === file && ! Open.exist?(file)
59
+ Path.setup file if String === file and not Path === file
59
60
  begin
60
61
  file = file.produce if Path === file
61
62
  raise "Could no produce file" if FalseClass === file
@@ -47,7 +47,7 @@ module TSV
47
47
  when :all
48
48
  "Index[#{target}]"
49
49
  else
50
- "Index[#{Log.fingerprint(fields)}->#{target}]"
50
+ "Index[#{Array === fields ? fields * "," : fields}->#{target}]"
51
51
  end
52
52
 
53
53
  prefix += select_prefix_str(kwargs[:select])
@@ -43,10 +43,12 @@ module TSV
43
43
  Log.debug log_message
44
44
  bar = log_message if TrueClass === bar
45
45
 
46
+ invert = select.delete :invert if Hash === select
46
47
  type_swap_tag = [type.to_s, @type.to_s] * "_"
47
48
  Log::ProgressBar.with_obj_bar(self, bar) do |bar|
48
49
  with_unnamed unnamed do
49
50
  each do |key,values|
51
+ next unless TSV.select key, values, select, invert: invert if select
50
52
  bar.tick if bar
51
53
  values = [values] if @type == :single
52
54
  if positions.nil?
@@ -1,11 +1,11 @@
1
1
  require 'matrix'
2
2
 
3
3
  module TSV
4
- def reorder(key_field = nil, fields = nil, merge: true, one2one: true, **kwargs)
5
- res = self.annotate({})
4
+ def reorder(key_field = nil, fields = nil, merge: true, one2one: true, data: nil, unnamed: true, **kwargs)
5
+ res = data || self.annotate({})
6
6
  res.type = kwargs[:type] if kwargs.include?(:type)
7
7
  kwargs[:one2one] = one2one
8
- key_field_name, field_names = with_unnamed do
8
+ key_field_name, field_names = with_unnamed unnamed do
9
9
  traverse key_field, fields, **kwargs do |k,v|
10
10
  if res.type == :double && merge && res.include?(k)
11
11
  current = res[k]
@@ -23,11 +23,11 @@ module TSV
23
23
  res[k] = merged
24
24
  end
25
25
  elsif res.type == :flat
26
- res[k] ||= []
27
26
  if merge == :concat
27
+ res[k] ||= []
28
28
  res[k].concat v
29
29
  else
30
- res[k] += v
30
+ res[k] = res[k].nil? ? v : res[k] + v
31
31
  end
32
32
  else
33
33
  res[k] = v
data/lib/scout/tsv.rb CHANGED
@@ -18,7 +18,6 @@ module TSV
18
18
  extend Annotation
19
19
  annotation :key_field, :fields, :type, :cast, :filename, :namespace, :unnamed, :identifiers, :serializer, :entity_options
20
20
 
21
-
22
21
  def self.str2options(str)
23
22
  field_options,_sep, rest = str.partition("#")
24
23
  key, fields_str = field_options.split("~")
@@ -251,7 +251,13 @@ module Workflow
251
251
  workload = new_workload
252
252
  sleep timer
253
253
  end
254
- all_jobs.each{|s| s.join }
254
+ all_jobs.each{|s|
255
+ begin
256
+ s.join
257
+ rescue
258
+ Log.warn "Job #{s.short_path} ended with exception #{$!.class.to_s}: #{$!.message}"
259
+ end
260
+ }
255
261
  rescue TryAgain
256
262
  retry
257
263
  end
@@ -275,9 +281,12 @@ module Workflow
275
281
  produce_list
276
282
  end
277
283
 
278
- def self.produce(jobs, produce_cpus: Etc.nprocessors, produce_timer: 5)
284
+ def self.produce(jobs, produce_cpus: Etc.nprocessors, produce_timer: 1)
279
285
  jobs = [jobs] unless Array === jobs
280
286
  orchestrator = Orchestrator.new produce_timer.to_i, cpus: produce_cpus.to_i
281
- orchestrator.process({}, jobs)
287
+ begin
288
+ orchestrator.process({}, jobs)
289
+ rescue Orchestrator::NoWork
290
+ end
282
291
  end
283
292
  end
@@ -2,11 +2,21 @@ require 'scout/entity'
2
2
  require 'scout/workflow'
3
3
 
4
4
  module EntityWorkflow
5
+ attr_accessor :entity_name
6
+
7
+ def entity_name=(name)
8
+ @entity_name = name
9
+ helper name do
10
+ entity
11
+ end
12
+ end
5
13
 
6
14
  def self.extended(base)
7
15
  base.extend Workflow
8
16
  base.extend Entity
9
17
 
18
+ base.entity_name ||= 'entity'
19
+
10
20
  base.instance_variable_set(:@annotation_inputs, IndiferentHash.setup({})) unless base.instance_variables.include?(:@annotation_inputs)
11
21
  class << base
12
22
  def annotation_input(name, type=nil, desc=nil, default=nil, options = {})
@@ -20,7 +30,7 @@ module EntityWorkflow
20
30
  base.setup(clean_name.dup, inputs.to_hash)
21
31
  end
22
32
 
23
- base.helper :entity_list do
33
+ base.helper :entity_list do
24
34
  list = inputs.last
25
35
  list = list.load if Step === list
26
36
  base.setup(list, inputs.to_hash)
@@ -46,12 +56,13 @@ module EntityWorkflow
46
56
  input annotation
47
57
  end
48
58
  end
59
+
49
60
  case property_type
50
61
  when :single, :single2array
51
- input :entity, :string, "#{self.to_s} identifier", nil, jobname: true
62
+ input entity_name, :string, "#{self.to_s} identifier", nil, jobname: true
52
63
  task(task_name => result_type, &block)
53
64
  when :both
54
- input :entity, :string, "#{self.to_s} identifier", nil, jobname: true
65
+ input entity_name, :string, "#{self.to_s} identifier", nil, jobname: true
55
66
  input :list, :array, "#{self.to_s} identifier list"
56
67
  task(task_name => result_type, &block)
57
68
  else
@@ -59,7 +70,8 @@ module EntityWorkflow
59
70
  task(task_name => result_type, &block)
60
71
  end
61
72
 
62
- property task_name => property_type do |*args|
73
+ property_name = task_name.to_s.sub(/^(#{entity_name}_list|#{entity_name}|list)_/, '')
74
+ property property_name => property_type do |*args|
63
75
  job = job(task_name, *args)
64
76
  Array === job ? job.collect(&:run) : job.run
65
77
  end
@@ -34,6 +34,15 @@ class Step
34
34
  @mutex.synchronize(&block)
35
35
  end
36
36
 
37
+ def provided_inputs
38
+ @provided_inputs ||= begin
39
+ if info_file && Open.exists?(info_file)
40
+ info[:provided_inputs]
41
+ else
42
+ {}
43
+ end
44
+ end
45
+ end
37
46
  def inputs
38
47
  @inputs ||= begin
39
48
  if info_file && Open.exists?(info_file)
@@ -174,6 +183,7 @@ class Step
174
183
  reset_info :status => :setup, :issued => Time.now,
175
184
  :pid => Process.pid, :pid_hostname => Misc.hostname,
176
185
  :task_name => task_name, :workflow => workflow.to_s,
186
+ :provided_inputs => Annotation.purge(provided_inputs),
177
187
  :inputs => Annotation.purge(inputs), :input_names => input_names, :type => type,
178
188
  :dependencies => (dependencies || []) .collect{|d| d.path }
179
189
 
@@ -83,11 +83,12 @@ module Task
83
83
 
84
84
  id = DEFAULT_NAME if id.nil?
85
85
 
86
+ sanitized_id = Path.sanitize_filename(id, 150)
86
87
  if non_default_inputs.any? && !(non_default_inputs == [jobname_input] && provided_inputs[jobname_input] == id)
87
88
  hash = Misc.digest(:inputs => input_digest_str, :dependencies => dependencies)
88
- name = [id, hash] * "_"
89
+ name = [sanitized_id, hash] * "_"
89
90
  else
90
- name = id
91
+ name = sanitized_id
91
92
  end
92
93
 
93
94
  extension = self.extension
@@ -139,14 +139,16 @@ module Workflow
139
139
  dep_tree = {}
140
140
  task = self.tasks[task_name]
141
141
  raise TaskNotFound, "Task #{task_name} in #{self.to_s}" if task.nil?
142
- task.deps.each do |workflow, task, options|
143
- next if seen.include? dep
144
- seen << [workflow, task, options.merge(seen_options)]
145
- next if task.nil?
146
142
 
147
- key = [workflow, task]
143
+ task.deps.each do |workflow, dep_task, options|
144
+ dep = [workflow, dep_task, options.merge(seen_options)]
145
+ #next if seen.include? dep
146
+ seen << dep
147
+ next if dep_task.nil?
148
148
 
149
- dep_tree[key] = workflow.dep_tree(task, seen, options.merge(seen_options))
149
+ dep_key = [workflow, dep_task]
150
+
151
+ dep_tree[dep_key] = workflow.dep_tree(dep_task, seen, options.merge(seen_options))
150
152
  end if task.deps
151
153
 
152
154
  @dep_tree[key] = dep_tree if save
@@ -163,6 +165,7 @@ module Workflow
163
165
  def _prov_tasks(tree)
164
166
  tasks = []
165
167
  heap = tree.values
168
+ heap = [tree]
166
169
  while heap.any?
167
170
  t = heap.pop
168
171
  t.each do |k,v|
@@ -188,10 +191,8 @@ module Workflow
188
191
  first = last.nil?
189
192
  last = _prov_tasks(workflow.dep_tree(task_name))
190
193
 
191
- break if child
192
-
193
194
  if child
194
- description << "->" << task_name.to_s
195
+ description << "->" << task_name.to_s
195
196
  elsif first
196
197
  description << "" << task_name.to_s
197
198
  else
@@ -204,7 +205,6 @@ module Workflow
204
205
  end
205
206
 
206
207
  def prov_tree(tree, offset = 0, seen = [])
207
-
208
208
  return "" if tree.empty?
209
209
 
210
210
  lines = []
@@ -133,10 +133,11 @@ module Workflow
133
133
  begin
134
134
  workflow_name, *subworkflows = complete_workflow_name.split("::")
135
135
  workflow_file = workflow_name
136
- workflow_file = Path.setup('workflows')[workflow_name]["workflow.rb"] unless Open.exists?(workflow_file)
137
- workflow_file = Path.setup('workflows')[Misc.snake_case(workflow_name)]["workflow.rb"] unless Open.exists?(workflow_file)
138
- workflow_file = Path.setup('workflows')[Misc.camel_case(workflow_name)]["workflow.rb"] unless Open.exists?(workflow_file)
139
- if Open.exists?(workflow_file)
136
+ workflow_file = Path.setup('workflows')[workflow_name]["workflow.rb"] unless Open.exists?(workflow_file) && ! Open.directory?(workflow_file)
137
+ workflow_file = Path.setup('workflows')[Misc.snake_case(workflow_name)]["workflow.rb"] unless Open.exists?(workflow_file) && ! Open.directory?(workflow_file)
138
+ workflow_file = Path.setup('workflows')[Misc.camel_case(workflow_name)]["workflow.rb"] unless Open.exists?(workflow_file) && ! Open.directory?(workflow_file)
139
+
140
+ if Open.exists?(workflow_file) && ! Open.directory?(workflow_file)
140
141
  self.main = nil
141
142
  require_workflow_file(workflow_file)
142
143
  elsif autoinstall
data/lib/scout-gear.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'scout-essentials'
2
2
  require_relative 'scout/tsv'
3
3
 
4
- Path.path_maps[:scout_gear] = File.join(Path.caller_lib_dir(__FILE__), "{TOPLEVEL}/{SUBPATH}")
4
+ Path.path_maps[:scout_gear_lib] = File.join(Path.caller_lib_dir(__FILE__), "{TOPLEVEL}/{SUBPATH}")
5
5
 
6
6
  Persist.cache_dir = Scout.var.cache.persistence
7
7
  TmpFile.tmpdir = Scout.tmp.find :user
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 10.7.13 ruby lib
5
+ # stub: scout-gear 10.8.1 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "scout-gear".freeze
9
- s.version = "10.7.13".freeze
9
+ s.version = "10.8.1".freeze
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 = "1980-01-02"
14
+ s.date = "2025-06-12"
15
15
  s.description = "Scout gear: workflow, TSVs, persistence, entities, associations, and knowledge_bases.".freeze
16
16
  s.email = "mikisvaz@gmail.com".freeze
17
17
  s.executables = ["scout".freeze]
@@ -250,7 +250,7 @@ Gem::Specification.new do |s|
250
250
  ]
251
251
  s.homepage = "http://github.com/mikisvaz/scout-gear".freeze
252
252
  s.licenses = ["MIT".freeze]
253
- s.rubygems_version = "3.6.7".freeze
253
+ s.rubygems_version = "3.6.6".freeze
254
254
  s.summary = "basic gear for scouts".freeze
255
255
 
256
256
  s.specification_version = 4
data/scout_commands/find CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'scout-gear'
4
- require 'scout/offsite'
5
4
 
6
5
  $0 = "scout #{$previous_commands.any? ? $previous_commands*" " + " " : "" }#{ File.basename(__FILE__) }" if $previous_commands
7
6
 
@@ -80,5 +79,5 @@ if where.nil? || where == 'all' || path.path_maps.include?(where.to_sym)
80
79
  puts location
81
80
  end
82
81
  else
83
- puts SSHLine.command(where, $0, ARGV, options.merge("where" => :all))
82
+ raise ParameterException, "Where '#{where}' not identified. Try scout-camp if looking for a remote file"
84
83
  end
@@ -40,6 +40,8 @@ queue_dir = Scout.var.queue
40
40
 
41
41
  class TrayAgain < Exception; end
42
42
 
43
+ options.keys_to_sym!
44
+
43
45
  begin
44
46
  if ARGV.empty?
45
47
  files = queue_dir.glob_all("*/*/*")
@@ -67,10 +69,9 @@ begin
67
69
  end
68
70
 
69
71
  jobs = files.collect{|file| Workflow.queue_job(file) }
72
+
73
+
70
74
  begin
71
- options.keys.each do |key|
72
- options[key.to_sym] = options.delete(key)
73
- end
74
75
  Workflow.produce(jobs, **options)
75
76
  rescue Workflow::Orchestrator::NoWork
76
77
  end
@@ -115,7 +115,8 @@ else
115
115
  when "serial"
116
116
  job.run(true)
117
117
  when "local"
118
- orchestrator = Workflow::Orchestrator.new 3, "cpus" => Misc.processors
118
+ refresh = Scout::Config.get :refresh, :deploy, :local, :orchestrator, default: 3
119
+ orchestrator = Workflow::Orchestrator.new refresh.to_f, "cpus" => Misc.processors
119
120
  orchestrator.process({}, job)
120
121
  when "slurm"
121
122
  require 'rbbt-scout'
@@ -374,5 +374,47 @@ row3 a
374
374
  assert Annotation::AnnotatedObject === data["row1"]
375
375
  end
376
376
  end
377
+
378
+ def test_tsv_traverse_select
379
+ content =<<-'EOF'
380
+ #: :sep=/\s+/#:type=:double
381
+ #Id ValueA ValueB OtherID
382
+ row1 a|aa|aaa b Id1|Id2
383
+ row2 A B Id3
384
+ row2 AA BB Id33
385
+ EOF
386
+
387
+ tsv = TmpFile.with_file(content) do |filename|
388
+ TSV.open(filename, :persist => true)
389
+ end
390
+
391
+ all_values = []
392
+ tsv.traverse "ValueA", :all, select: {ValueA: "A"} do |k,v|
393
+ all_values.concat(v)
394
+ end
395
+ refute all_values.flatten.include? "row1"
396
+ refute all_values.flatten.include? "a"
397
+ refute all_values.flatten.include? "aaa"
398
+
399
+ assert_include all_values.flatten, "row2"
400
+
401
+ tsv.traverse "ValueA", :all, select: {ValueA: "a", invert: true} do |k,v|
402
+ all_values.concat(v)
403
+ end
404
+ refute all_values.flatten.include? "row1"
405
+ refute all_values.flatten.include? "a"
406
+ refute all_values.flatten.include? "aaa"
407
+
408
+ assert_include all_values.flatten, "row2"
409
+
410
+ all_values = []
411
+ tsv.traverse "Id", :all do |k,v|
412
+ all_values.concat(v)
413
+ end
414
+ assert_include all_values.flatten, "row1"
415
+ assert_include all_values.flatten, "a"
416
+ assert_include all_values.flatten, "aaa"
417
+ end
418
+
377
419
  end
378
420
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scout-gear
3
3
  version: !ruby/object:Gem::Version
4
- version: 10.7.13
4
+ version: 10.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miguel Vazquez
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
10
+ date: 2025-06-12 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: scout-essentials
@@ -334,7 +334,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
334
334
  - !ruby/object:Gem::Version
335
335
  version: '0'
336
336
  requirements: []
337
- rubygems_version: 3.6.7
337
+ rubygems_version: 3.6.6
338
338
  specification_version: 4
339
339
  summary: basic gear for scouts
340
340
  test_files: []