scout-gear 7.3.0 → 8.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. checksums.yaml +4 -4
  2. data/.vimproject +44 -16
  3. data/Rakefile +6 -1
  4. data/VERSION +1 -1
  5. data/bin/scout +21 -7
  6. data/doc/lib/scout/path.md +35 -0
  7. data/doc/lib/scout/workflow/task.md +13 -0
  8. data/lib/rbbt-scout.rb +1 -0
  9. data/lib/scout/cmd.rb +24 -25
  10. data/lib/scout/concurrent_stream.rb +59 -39
  11. data/lib/scout/config.rb +1 -1
  12. data/lib/scout/exceptions.rb +10 -0
  13. data/lib/scout/log/color.rb +15 -12
  14. data/lib/scout/log/progress/report.rb +8 -6
  15. data/lib/scout/log/progress/util.rb +61 -54
  16. data/lib/scout/log/progress.rb +1 -1
  17. data/lib/scout/log/trap.rb +107 -0
  18. data/lib/scout/log.rb +115 -52
  19. data/lib/scout/meta_extension.rb +47 -6
  20. data/lib/scout/misc/digest.rb +12 -3
  21. data/lib/scout/misc/format.rb +24 -7
  22. data/lib/scout/misc/insist.rb +1 -1
  23. data/lib/scout/misc/monitor.rb +22 -0
  24. data/lib/scout/misc/system.rb +58 -0
  25. data/lib/scout/named_array.rb +73 -3
  26. data/lib/scout/offsite/ssh.rb +171 -0
  27. data/lib/scout/offsite/step.rb +83 -0
  28. data/lib/scout/offsite/sync.rb +55 -0
  29. data/lib/scout/offsite.rb +3 -0
  30. data/lib/scout/open/lock/lockfile.rb +587 -0
  31. data/lib/scout/open/lock.rb +9 -2
  32. data/lib/scout/open/remote.rb +16 -1
  33. data/lib/scout/open/stream.rb +146 -83
  34. data/lib/scout/open/util.rb +22 -3
  35. data/lib/scout/open.rb +5 -4
  36. data/lib/scout/path/find.rb +24 -11
  37. data/lib/scout/path/util.rb +40 -0
  38. data/lib/scout/persist/serialize.rb +19 -6
  39. data/lib/scout/persist.rb +29 -13
  40. data/lib/scout/resource/path.rb +57 -0
  41. data/lib/scout/resource/produce.rb +0 -8
  42. data/lib/scout/resource/util.rb +12 -5
  43. data/lib/scout/tmpfile.rb +7 -8
  44. data/lib/scout/tsv/attach.rb +177 -0
  45. data/lib/scout/tsv/change_id.rb +40 -0
  46. data/lib/scout/tsv/dumper.rb +74 -46
  47. data/lib/scout/tsv/index.rb +85 -87
  48. data/lib/scout/tsv/open.rb +160 -85
  49. data/lib/scout/tsv/parser.rb +142 -80
  50. data/lib/scout/tsv/path.rb +1 -2
  51. data/lib/scout/tsv/persist/adapter.rb +15 -45
  52. data/lib/scout/tsv/persist/fix_width_table.rb +3 -0
  53. data/lib/scout/tsv/persist/tokyocabinet.rb +6 -1
  54. data/lib/scout/tsv/persist.rb +4 -0
  55. data/lib/scout/tsv/stream.rb +204 -0
  56. data/lib/scout/tsv/transformer.rb +152 -0
  57. data/lib/scout/tsv/traverse.rb +96 -92
  58. data/lib/scout/tsv/util/filter.rb +9 -0
  59. data/lib/scout/tsv/util/reorder.rb +81 -0
  60. data/lib/scout/tsv/util/select.rb +78 -33
  61. data/lib/scout/tsv/util/unzip.rb +86 -0
  62. data/lib/scout/tsv/util.rb +60 -11
  63. data/lib/scout/tsv.rb +34 -4
  64. data/lib/scout/work_queue/socket.rb +6 -1
  65. data/lib/scout/work_queue/worker.rb +5 -2
  66. data/lib/scout/work_queue.rb +51 -20
  67. data/lib/scout/workflow/definition.rb +23 -3
  68. data/lib/scout/workflow/deployment/orchestrator.rb +245 -0
  69. data/lib/scout/workflow/deployment.rb +1 -0
  70. data/lib/scout/workflow/step/dependencies.rb +56 -10
  71. data/lib/scout/workflow/step/file.rb +5 -0
  72. data/lib/scout/workflow/step/info.rb +40 -7
  73. data/lib/scout/workflow/step/load.rb +1 -1
  74. data/lib/scout/workflow/step/provenance.rb +9 -7
  75. data/lib/scout/workflow/step/status.rb +43 -0
  76. data/lib/scout/workflow/step.rb +160 -49
  77. data/lib/scout/workflow/task/dependencies.rb +114 -0
  78. data/lib/scout/workflow/task/inputs.rb +40 -32
  79. data/lib/scout/workflow/task.rb +38 -102
  80. data/lib/scout/workflow/usage.rb +48 -18
  81. data/lib/scout/workflow.rb +4 -2
  82. data/lib/scout-gear.rb +2 -0
  83. data/lib/scout.rb +6 -0
  84. data/scout-gear.gemspec +52 -23
  85. data/scout_commands/doc +37 -0
  86. data/scout_commands/find +1 -0
  87. data/scout_commands/offsite +30 -0
  88. data/scout_commands/update +29 -0
  89. data/scout_commands/workflow/info +15 -3
  90. data/scout_commands/workflow/install +102 -0
  91. data/scout_commands/workflow/task +57 -9
  92. data/test/scout/offsite/test_ssh.rb +15 -0
  93. data/test/scout/offsite/test_step.rb +33 -0
  94. data/test/scout/offsite/test_sync.rb +36 -0
  95. data/test/scout/offsite/test_task.rb +0 -0
  96. data/test/scout/open/test_stream.rb +60 -58
  97. data/test/scout/path/test_find.rb +10 -1
  98. data/test/scout/resource/test_path.rb +6 -0
  99. data/test/scout/resource/test_produce.rb +15 -0
  100. data/test/scout/test_meta_extension.rb +25 -0
  101. data/test/scout/test_named_array.rb +24 -0
  102. data/test/scout/test_persist.rb +9 -2
  103. data/test/scout/test_tsv.rb +229 -2
  104. data/test/scout/test_work_queue.rb +65 -41
  105. data/test/scout/tsv/persist/test_tokyocabinet.rb +29 -1
  106. data/test/scout/tsv/test_attach.rb +227 -0
  107. data/test/scout/tsv/test_change_id.rb +98 -0
  108. data/test/scout/tsv/test_dumper.rb +1 -1
  109. data/test/scout/tsv/test_index.rb +49 -3
  110. data/test/scout/tsv/test_open.rb +160 -2
  111. data/test/scout/tsv/test_parser.rb +33 -2
  112. data/test/scout/tsv/test_persist.rb +2 -0
  113. data/test/scout/tsv/test_stream.rb +200 -0
  114. data/test/scout/tsv/test_transformer.rb +120 -0
  115. data/test/scout/tsv/test_traverse.rb +88 -3
  116. data/test/scout/tsv/test_util.rb +1 -0
  117. data/test/scout/tsv/util/test_reorder.rb +94 -0
  118. data/test/scout/tsv/util/test_select.rb +25 -11
  119. data/test/scout/tsv/util/test_unzip.rb +112 -0
  120. data/test/scout/work_queue/test_socket.rb +0 -1
  121. data/test/scout/workflow/deployment/test_orchestrator.rb +272 -0
  122. data/test/scout/workflow/step/test_dependencies.rb +68 -0
  123. data/test/scout/workflow/step/test_info.rb +18 -0
  124. data/test/scout/workflow/step/test_status.rb +30 -0
  125. data/test/scout/workflow/task/test_dependencies.rb +355 -0
  126. data/test/scout/workflow/task/test_inputs.rb +67 -14
  127. data/test/scout/workflow/test_definition.rb +18 -0
  128. data/test/scout/workflow/test_documentation.rb +24 -0
  129. data/test/scout/workflow/test_step.rb +112 -3
  130. data/test/scout/workflow/test_task.rb +0 -151
  131. data/test/scout/workflow/test_usage.rb +33 -6
  132. data/test/test_scout.rb +9 -0
  133. metadata +100 -8
  134. data/scout_commands/workflow/task_old +0 -706
@@ -0,0 +1,204 @@
1
+ module TSV
2
+ def self.paste_streams(streams, type: nil, sort: nil, sort_memory: nil, sep: nil, preamble: nil, header: nil, same_fields: nil, fix_flat: nil, all_match: nil, field_prefix: nil)
3
+
4
+ streams = streams.collect do |stream|
5
+ case stream
6
+ when(defined? Step and Step)
7
+ stream.stream
8
+ when Path
9
+ stream.open
10
+ when TSV::Dumper
11
+ stream.stream
12
+ else
13
+ stream
14
+ end
15
+ end.compact
16
+
17
+ num_streams = streams.length
18
+
19
+ streams = streams.collect do |stream|
20
+ Open.sort_stream(stream, memory: sort_memory)
21
+ end if sort
22
+
23
+ begin
24
+
25
+ lines =[]
26
+ fields =[]
27
+ sizes =[]
28
+ key_fields =[]
29
+ input_options =[]
30
+ empty =[]
31
+ preambles =[]
32
+ parser_types =[]
33
+
34
+ type ||= :double
35
+
36
+ streams = streams.collect do |stream|
37
+
38
+ parser = TSV::Parser.new stream, type: type
39
+
40
+ sfields = parser.fields
41
+
42
+ if field_prefix
43
+ index = streams.index stream
44
+ prefix = field_prefix[index]
45
+
46
+ sfields = sfields.collect{|f|[prefix, f]* ":"}
47
+ end
48
+
49
+ first_line = parser.first_line
50
+ first_line = nil if first_line == ""
51
+
52
+ lines << first_line
53
+ key_fields << parser.key_field
54
+ fields << sfields
55
+ sizes << sfields.length if sfields
56
+ input_options << parser.options
57
+ preambles << parser.preamble if preamble and not parser.preamble.empty?
58
+ parser_types << parser.type
59
+
60
+ empty << stream if parser.first_line.nil? || parser.first_line.empty?
61
+
62
+ stream
63
+ end
64
+
65
+
66
+ all_fields = fields.dup
67
+
68
+ key_field = key_fields.compact.first
69
+
70
+ if same_fields
71
+ fields = fields.first
72
+ else
73
+ fields = fields.compact.flatten
74
+ end
75
+
76
+ options = input_options.first
77
+ type ||= options[:type]
78
+ type ||= :list if type == :single
79
+ type ||= :double if type == :flat
80
+
81
+ preamble_txt = case preamble
82
+ when TrueClass
83
+ preambles * "\n"
84
+ when String
85
+ if preamble[0]== '+'
86
+ preambles * "\n" + "\n" + preamble[1..-1]
87
+ else
88
+ preamble
89
+ end
90
+ else
91
+ nil
92
+ end
93
+
94
+ empty_pos = empty.collect{|stream| streams.index stream}
95
+
96
+ keys =[]
97
+ parts =[]
98
+ lines.each_with_index do |line,i|
99
+ if line.nil? || line.empty?
100
+ keys[i]= nil
101
+ parts[i]= nil
102
+ else
103
+ vs = line.chomp.split(sep, -1)
104
+ key, *p = vs
105
+ keys[i]= key
106
+ parts[i]= p
107
+ end
108
+ sizes[i]||= parts[i].length-1 unless parts[i].nil?
109
+ end
110
+ done_streams =[]
111
+
112
+ dumper = TSV::Dumper.new key_field: key_field, fields: fields, type: type
113
+ dumper.init
114
+
115
+ t = Thread.new do
116
+ Thread.report_on_exception = false
117
+ Thread.current["name"] = "Paste streams"
118
+
119
+ last_min = nil
120
+ while lines.reject{|line| line.nil?}.any?
121
+ min = keys.compact.sort.first
122
+ break if min.nil?
123
+ new_values =[]
124
+
125
+ skip = all_match && keys.uniq !=[min]
126
+
127
+ keys.each_with_index do |key,i|
128
+ case key
129
+ when min
130
+ new_values << parts[i]
131
+
132
+ begin
133
+ line = lines[i]= begin
134
+ streams[i].gets
135
+ rescue
136
+ Log.exception $!
137
+ nil
138
+ end
139
+ if line.nil?
140
+ keys[i]= nil
141
+ parts[i]= nil
142
+ else
143
+ k, *p = line.chomp.split(sep, -1)
144
+ raise TryAgain if k == keys[i]
145
+ keys[i]= k
146
+ parts[i]= p.collect{|e| e.nil? ? "" : e}
147
+ end
148
+ rescue TryAgain
149
+ keys[i]= nil
150
+ parts[i]= nil
151
+ Log.debug "Skipping repeated key in stream #{i}: #{keys[i]}"
152
+ retry
153
+ end
154
+ else
155
+ p = [nil] * sizes[i]
156
+ new_values << p
157
+ end
158
+ end
159
+
160
+ next if skip
161
+
162
+ if same_fields
163
+ new_values_same = []
164
+ new_values.each do |list|
165
+ list.each_with_index do |l,i|
166
+ new_values_same[i] ||= []
167
+ new_values_same[i] << l
168
+ end
169
+ end
170
+ new_values = new_values_same
171
+ else
172
+ new_values = new_values.inject([]){|acc,l| acc.concat l }
173
+ end
174
+
175
+ dumper.add min, new_values
176
+ end
177
+
178
+ dumper.close
179
+
180
+ streams.each do |stream|
181
+ stream.close if stream.respond_to?(:close) && ! stream.closed?
182
+ stream.join if stream.respond_to? :join
183
+ end
184
+ end
185
+ rescue Aborted
186
+ Log.error "Aborted pasting streams #{streams.inspect}: #{$!.message}"
187
+ streams.each do |stream|
188
+ stream.abort if stream.respond_to? :abort
189
+ end
190
+ raise $!
191
+ rescue Exception
192
+ Log.error "Exception pasting streams #{streams.inspect}: #{$!.message}"
193
+ streams.each do |stream|
194
+ stream.abort if stream.respond_to? :abort
195
+ end
196
+ raise $!
197
+ end
198
+
199
+ Thread.pass until t["name"]
200
+
201
+ ConcurrentStream.setup(dumper.stream, threads: [t])
202
+ end
203
+
204
+ end
@@ -0,0 +1,152 @@
1
+ module TSV
2
+ class Transformer
3
+ attr_accessor :unnamed, :parser, :dumper
4
+
5
+ def initialize(parser, dumper = nil, unnamed: false)
6
+ if TSV::Parser === parser
7
+ @parser = parser
8
+ elsif TSV === parser
9
+ @parser = parser
10
+ else
11
+ @parser = TSV::Parser.new parser
12
+ end
13
+ @unnamed = unnamed
14
+ if dumper.nil?
15
+ @dumper = TSV::Dumper.new(@parser)
16
+ @dumper.sep = "\t"
17
+ else
18
+ @dumper = dumper
19
+ end
20
+ end
21
+
22
+ def key_field=(key_field)
23
+ @dumper.key_field = key_field
24
+ end
25
+
26
+ def fields=(fields)
27
+ @dumper.fields = fields
28
+ end
29
+
30
+ def type=(type)
31
+ @dumper.type = type
32
+ end
33
+
34
+ def type
35
+ @dumper.type
36
+ end
37
+
38
+ def sep=(sep)
39
+ @dumper.sep = sep
40
+ end
41
+
42
+ def include?(*args)
43
+ false
44
+ end
45
+
46
+ def key_field
47
+ @dumper.key_field
48
+ end
49
+
50
+ def fields
51
+ @dumper.fields
52
+ end
53
+
54
+ def all_fields
55
+ return nil if fields.nil?
56
+ [key_field] + fields
57
+ end
58
+
59
+ def options
60
+ @dumper.options
61
+ end
62
+
63
+ def identify_field(name)
64
+ TSV.identify_field key_field, fields, name
65
+ end
66
+
67
+ def traverse(*args, **kwargs, &block)
68
+ kwargs[:into] = @dumper
69
+ kwargs[:bar] = "Transform #{Log.fingerprint @parser} into #{Log.fingerprint @target}" if TrueClass === kwargs[:bar]
70
+ @dumper.init if @dumper.respond_to?(:init) && ! @dumper.initialized
71
+ Log.debug "Transform #{Log.fingerprint @parser} into #{Log.fingerprint @dumper}"
72
+ Open.traverse(@parser, *args, **kwargs) do |k,v|
73
+ NamedArray.setup(v, @parser.fields, k) unless @unnamed
74
+ block.call k, v
75
+ end
76
+ end
77
+
78
+ def each(*args, **kwargs, &block)
79
+ kwargs[:into] = @dumper
80
+ kwargs[:bar] = "Transform #{Log.fingerprint @parser} into #{Log.fingerprint @target}" if TrueClass === kwargs[:bar]
81
+ @dumper.init if @dumper.respond_to?(:init) && ! @dumper.initialized
82
+ Open.traverse(@parser, *args, **kwargs) do |k,v|
83
+ NamedArray.setup(v, @parser.fields, k) unless @unnamed
84
+ block.call k, v
85
+ [k, v]
86
+ end
87
+ end
88
+
89
+ def with_unnamed
90
+ begin
91
+ old_unnamed = @unnamed
92
+ @unnamed = true
93
+ yield
94
+ ensure
95
+ @unnamed = old_unnamed
96
+ end
97
+ end
98
+
99
+ def []=(key, value)
100
+ @dumper.init if @dumper.respond_to?(:init) && ! @dumper.initialized
101
+ @dumper.add key, value
102
+ end
103
+
104
+ def stream
105
+ @dumper.stream
106
+ end
107
+
108
+ def tsv(*args)
109
+ TSV === @dumper ? @dumper : TSV.open(stream, *args)
110
+ end
111
+ end
112
+
113
+ def to_list
114
+ res = self.annotate({})
115
+ transformer = Transformer.new self, res
116
+ transformer.type = :list
117
+ transformer.traverse do |k,v|
118
+ case self.type
119
+ when :single
120
+ [k, [v]]
121
+ when :double
122
+ [k, v.collect{|v| v.first }]
123
+ when :flat
124
+ [k, v.slice(0,1)]
125
+ end
126
+ end
127
+ res
128
+ end
129
+
130
+ def to_single
131
+ res = self.annotate({})
132
+ transformer = Transformer.new self, res
133
+ transformer.type = :single
134
+ transformer.traverse do |k,v|
135
+ v = v.first while Array === v
136
+ [k, v]
137
+ end
138
+ res
139
+ end
140
+
141
+ def to_flat
142
+ res = self.annotate({})
143
+ transformer = Transformer.new self, res
144
+ transformer.type = :flat
145
+ transformer.traverse do |k,v|
146
+ v = Array === v ? v.flatten : [v]
147
+ [k, v]
148
+ end
149
+ res
150
+ end
151
+ end
152
+
@@ -1,22 +1,14 @@
1
1
  require_relative 'parser'
2
2
  module TSV
3
- def self.identify_field(key_field, fields, name)
4
- return :key if name == :key || key_field.start_with?(name.to_s)
5
- name.collect!{|n| key_field == n ? :key : n } if Array === name
6
- NamedArray.identify_name(fields, name)
7
- end
8
-
9
- def identify_field(name)
10
- TSV.identify_field(@key_field, @fields, name)
11
- end
12
-
13
- def traverse(key_field_pos = :key, fields_pos = nil, type: nil, one2one: false, unnamed: false, key_field: nil, fields: nil, &block)
3
+ def traverse(key_field_pos = :key, fields_pos = nil, type: nil, one2one: false, unnamed: false, key_field: nil, fields: nil, bar: false, cast: nil, select: nil, &block)
14
4
  key_field = key_field_pos if key_field.nil?
15
- fields = fields_pos if fields.nil?
5
+ fields = fields_pos.dup if fields.nil?
16
6
  type = @type if type.nil?
17
7
  key_pos = self.identify_field(key_field)
8
+ fields = self.all_fields if fields == :all
18
9
  fields = [fields] unless fields.nil? || Array === fields
19
- positions = fields.nil? ? nil : self.identify_field(fields)
10
+ positions = fields.nil? || fields == :all ? nil : self.identify_field(fields)
11
+
20
12
 
21
13
  if key_pos == :key
22
14
  key_name = @key_field
@@ -33,7 +25,7 @@ module TSV
33
25
  field_names = @fields
34
26
  elsif positions.nil? && key_pos != :key
35
27
  field_names = @fields.dup
36
- field_names.delete_at key_pos
28
+ field_names.delete_at key_pos unless fields == :all
37
29
  elsif positions.include?(:key)
38
30
  field_names = positions.collect{|p| p == :key ? @key_field : @fields[p] }
39
31
  else
@@ -43,98 +35,110 @@ module TSV
43
35
  key_index = positions.index :key if positions
44
36
  positions.delete :key if positions
45
37
 
46
- each do |key,values|
47
- values = [values] if @type == :single
48
- if positions.nil?
49
- if key_pos != :key
50
- values = values.dup
51
- key = values.delete_at(key_pos)
52
- end
53
- else
54
- orig_key = key
55
- key = values[key_pos] if key_pos != :key
38
+ log_message = "Traverse #{Log.fingerprint self}"
39
+ Log.debug log_message
40
+ bar = log_message if TrueClass === bar
56
41
 
57
- values = values.values_at(*positions)
58
- if key_index
59
- if @type == :double
60
- values.insert key_index, [orig_key]
61
- else
62
- values.insert key_index, orig_key
42
+ Log::ProgressBar.with_obj_bar(self, bar) do |bar|
43
+ with_unnamed unnamed do
44
+ each do |key,values|
45
+ bar.tick if bar
46
+ values = [values] if @type == :single
47
+ if positions.nil?
48
+ if key_pos != :key
49
+ values = values.dup
50
+ key = values.delete_at(key_pos)
51
+ end
52
+ else
53
+ orig_key = key
54
+ key = values[key_pos] if key_pos != :key
55
+
56
+ values = values.values_at(*positions)
57
+ if key_index
58
+ if @type == :double
59
+ values.insert key_index, [orig_key]
60
+ else
61
+ values.insert key_index, orig_key
62
+ end
63
+ end
63
64
  end
64
- end
65
- end
66
65
 
67
- if Array === key
68
- if @type == :double && one2one
69
- if one2one == :fill
70
- key.each_with_index do |key_i,i|
71
- if type == :double
72
- v_i = values.collect{|v| [v[i] || v.first] }
66
+ values = TSV.cast_value(values, cast) if cast
67
+
68
+ if Array === key
69
+ if @type == :double && one2one
70
+ if one2one == :strict
71
+ key.each_with_index do |key_i,i|
72
+ if type == :double
73
+ v_i = values.collect{|v| [v[i]] }
74
+ else
75
+ v_i = values.collect{|v| v[i] }
76
+ end
77
+ yield key_i, v_i
78
+ end
73
79
  else
74
- v_i = values.collect{|v| v[i] || v.first }
80
+ key.each_with_index do |key_i,i|
81
+ if type == :double
82
+ v_i = values.collect{|v| [v[i] || v.first] }
83
+ else
84
+ v_i = values.collect{|v| v[i] || v.first }
85
+ end
86
+ yield key_i, v_i, @fields
87
+ end
88
+ end
89
+ else
90
+ key.each_with_index do |key_i, i|
91
+ if type == :double
92
+ yield key_i, values
93
+ elsif type == :list
94
+ yield key_i, values.collect{|v| v[i] }
95
+ elsif type == :flat
96
+ yield key_i, values.flatten
97
+ elsif type == :single
98
+ yield key_i, values.first
99
+ end
75
100
  end
76
- yield key_i, v_i
77
101
  end
78
102
  else
79
- key.each_with_index do |key_i,i|
80
- if type == :double
81
- v_i = values.collect{|v| [v[i]] }
103
+ if type == @type
104
+ if type == :single
105
+ yield key, values.first
82
106
  else
83
- v_i = values.collect{|v| v[i] }
107
+ yield key, values
108
+ end
109
+ else
110
+ case [type, @type]
111
+ when [:double, :list]
112
+ yield key, values.collect{|v| [v] }
113
+ when [:double, :flat]
114
+ yield key, [values]
115
+ when [:double, :single]
116
+ yield key, [values]
117
+ when [:list, :double]
118
+ yield key, values.collect{|v| v.first }
119
+ when [:list, :flat]
120
+ yield key, [values.first]
121
+ when [:list, :single]
122
+ yield key, values
123
+ when [:flat, :double]
124
+ yield key, values.flatten
125
+ when [:flat, :list]
126
+ yield key, values.flatten
127
+ when [:flat, :single]
128
+ yield key, values
129
+ when [:single, :double]
130
+ yield key, values.flatten.first
131
+ when [:single, :list]
132
+ yield key, values.first
133
+ when [:single, :flat]
134
+ yield key, values.first
84
135
  end
85
- yield key_i, v_i
86
- end
87
- end
88
- else
89
- key.each_with_index do |key_i, i|
90
- if type == :double
91
- yield key_i, values
92
- elsif type == :list
93
- yield key_i, values.collect{|v| v[i] }
94
- elsif type == :flat
95
- yield key_i, values.flatten
96
- elsif type == :single
97
- yield key_i, values.first
98
136
  end
99
137
  end
100
138
  end
101
- else
102
- if type == @type
103
- if type == :single
104
- yield key, values.first
105
- else
106
- yield key, values
107
- end
108
- else
109
- case [type, @type]
110
- when [:double, :list]
111
- yield key, values.collect{|v| [v] }
112
- when [:double, :flat]
113
- yield key, [values]
114
- when [:double, :single]
115
- yield key, [values]
116
- when [:list, :double]
117
- yield key, values.collect{|v| v.first }
118
- when [:list, :flat]
119
- yield key, [values.first]
120
- when [:list, :single]
121
- yield key, values
122
- when [:flat, :double]
123
- yield key, values.flatten
124
- when [:flat, :list]
125
- yield key, values.flatten
126
- when [:flat, :single]
127
- yield key, values
128
- when [:single, :double]
129
- yield key, values.flatten.first
130
- when [:single, :list]
131
- yield key, values.first
132
- when [:single, :flat]
133
- yield key, values.first
134
- end
135
- end
136
139
  end
137
140
  end
141
+
138
142
 
139
143
  [key_name, field_names]
140
144
  end
@@ -299,5 +299,14 @@ module TSV
299
299
  FileUtils.rm f
300
300
  end
301
301
  end
302
+
303
+ def with_filters(filters, &block)
304
+ filter
305
+ begin
306
+ filters.each{|field,value| add_filter field, value }
307
+ ensure
308
+ reset_filters
309
+ end
310
+ end
302
311
  end
303
312
 
@@ -0,0 +1,81 @@
1
+ require 'matrix'
2
+
3
+ module TSV
4
+ def reorder(key_field = nil, fields = nil, merge: true, one2one: true, **kwargs)
5
+ res = self.annotate({})
6
+ res.type = kwargs[:type] if kwargs.include?(:type)
7
+ kwargs[:one2one] = one2one
8
+ key_field_name, field_names = traverse key_field, fields, **kwargs do |k,v|
9
+ if @type == :double && merge && res.include?(k)
10
+ current = res[k]
11
+ if merge == :concat
12
+ v.each_with_index do |new,i|
13
+ next if new.empty?
14
+ current[i].concat(new)
15
+ end
16
+ else
17
+ merged = []
18
+ v.each_with_index do |new,i|
19
+ next if new.empty?
20
+ merged[i] = current[i] + new
21
+ end
22
+ res[k] = merged
23
+ end
24
+ else
25
+ res[k] = v
26
+ end
27
+ end
28
+ res.key_field = key_field_name
29
+ res.fields = field_names
30
+ res
31
+ end
32
+
33
+ def slice(fields, **kwargs)
34
+ reorder :key, fields, **kwargs
35
+ end
36
+
37
+ def column(field, **kwargs)
38
+ new_type = case type
39
+ when :double, :flat
40
+ :flat
41
+ else
42
+ :single
43
+ end
44
+
45
+ kwargs[:type] = new_type
46
+ slice(field, **kwargs)
47
+ end
48
+
49
+ def transpose_list(key_field="Unkown ID")
50
+ new_fields = keys.dup
51
+ new = self.annotate({})
52
+ TSV.setup(new, :key_field => key_field, :fields => new_fields, :type => type, :filename => filename, :identifiers => identifiers)
53
+
54
+ m = Matrix.rows values
55
+ new_rows = m.transpose.to_a
56
+
57
+ fields.zip(new_rows) do |key,row|
58
+ new[key] = row
59
+ end
60
+
61
+ new
62
+ end
63
+
64
+ def transpose_double(key_field = "Unkown ID")
65
+ sep = "-!SEP--#{rand 10000}!-"
66
+ tmp = self.to_list{|v| v * sep}
67
+ new = tmp.transpose_list(key_field)
68
+ new.to_double{|v| v.split(sep)}
69
+ end
70
+
71
+ def transpose(key_field = "Unkown ID")
72
+ case type
73
+ when :single, :flat
74
+ self.to_list.transpose_list key_field
75
+ when :list
76
+ transpose_list key_field
77
+ when :double
78
+ transpose_double key_field
79
+ end
80
+ end
81
+ end