scout-gear 10.8.4 → 10.9.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 +4 -4
- data/.vimproject +13 -0
- data/README.md +352 -0
- data/VERSION +1 -1
- data/doc/Association.md +288 -0
- data/doc/Entity.md +296 -0
- data/doc/KnowledgeBase.md +433 -0
- data/doc/Persist.md +356 -0
- data/doc/Semaphore.md +171 -0
- data/doc/TSV.md +449 -0
- data/doc/WorkQueue.md +359 -0
- data/doc/Workflow.md +586 -0
- data/lib/scout/association.rb +4 -2
- data/lib/scout/entity/identifiers.rb +1 -1
- data/lib/scout/entity/object.rb +1 -1
- data/lib/scout/entity/property.rb +5 -5
- data/lib/scout/entity.rb +1 -1
- data/lib/scout/knowledge_base/description.rb +1 -1
- data/lib/scout/knowledge_base/list.rb +7 -2
- data/lib/scout/knowledge_base/registry.rb +2 -2
- data/lib/scout/knowledge_base.rb +20 -2
- data/lib/scout/monitor.rb +10 -6
- data/lib/scout/persist/engine/packed_index.rb +2 -2
- data/lib/scout/persist/engine/sharder.rb +1 -1
- data/lib/scout/persist/tsv.rb +1 -0
- data/lib/scout/semaphore.rb +1 -1
- data/lib/scout/tsv/dumper.rb +3 -3
- data/lib/scout/tsv/open.rb +1 -0
- data/lib/scout/tsv/parser.rb +1 -1
- data/lib/scout/tsv/transformer.rb +1 -0
- data/lib/scout/tsv/util.rb +2 -2
- data/lib/scout/work_queue/socket.rb +1 -1
- data/lib/scout/work_queue/worker.rb +7 -5
- data/lib/scout/workflow/entity.rb +22 -1
- data/lib/scout/workflow/step/config.rb +3 -3
- data/lib/scout/workflow/step/file.rb +4 -0
- data/lib/scout/workflow/step/info.rb +8 -2
- data/lib/scout/workflow/step.rb +10 -5
- data/lib/scout/workflow/task/inputs.rb +1 -1
- data/lib/scout/workflow/usage.rb +3 -2
- data/lib/scout/workflow/util.rb +22 -0
- data/scout-gear.gemspec +16 -5
- data/scout_commands/cat +86 -0
- data/scout_commands/doc +3 -1
- data/scout_commands/entity +151 -0
- data/scout_commands/system/status +238 -0
- data/scout_commands/workflow/info +23 -10
- data/scout_commands/workflow/install +1 -1
- data/test/scout/entity/test_property.rb +1 -1
- data/test/scout/knowledge_base/test_registry.rb +19 -0
- data/test/scout/test_work_queue.rb +1 -1
- data/test/scout/work_queue/test_worker.rb +12 -10
- metadata +15 -4
- data/doc/lib/scout/path.md +0 -35
- data/doc/lib/scout/workflow/task.md +0 -13
data/doc/TSV.md
ADDED
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
# TSV
|
|
2
|
+
|
|
3
|
+
TSV is a flexible table abstraction for delimited data with rich metadata, streaming, persistence, and transformation utilities. It provides:
|
|
4
|
+
|
|
5
|
+
- A typed table model with explicit key_field and fields, supporting four shapes: :double, :list, :flat, :single.
|
|
6
|
+
- Robust parsing from files, streams or strings (with header options), plus CSV support.
|
|
7
|
+
- Streaming writers (Dumper) and transformation pipelines (Transformer).
|
|
8
|
+
- Parallel traversal (cpus) with WorkQueue and thread-safe streaming via ConcurrentStream.
|
|
9
|
+
- Index builders (exact, point and range indexes) for fast lookups.
|
|
10
|
+
- Identifier translation and key/field re-mapping (change_key/change_id/translate).
|
|
11
|
+
- Table joins/attachments (attach) with fuzzy field matching, identifier indices and completion.
|
|
12
|
+
- Streaming table operations (paste/concat/collapse).
|
|
13
|
+
- Column operations: reorder/slice/column/transpose; melt, add_field, process, remove_duplicates, sorting, paging.
|
|
14
|
+
- Filters for on-disk or in-memory row selection that transparently affect iteration.
|
|
15
|
+
- Integration with Annotation/Entity (field-aware NamedArray on rows, entity-typed values).
|
|
16
|
+
- Path helpers and persistence through ScoutCabinet/TokyoCabinet.
|
|
17
|
+
|
|
18
|
+
Sections:
|
|
19
|
+
- Data model and setup
|
|
20
|
+
- Parsing and opening
|
|
21
|
+
- Dumper (writing) and Transformer (pipelines)
|
|
22
|
+
- Traversal and parallelization
|
|
23
|
+
- Indexing and position/range indices
|
|
24
|
+
- Identifier translation and key/field changes
|
|
25
|
+
- Attaching tables
|
|
26
|
+
- Streaming utilities (paste/concat/collapse)
|
|
27
|
+
- CSV utilities
|
|
28
|
+
- Column and row utilities (process/add_field/melt/select/reorder/transpose/unzip)
|
|
29
|
+
- Filters
|
|
30
|
+
- Annotation integration (annotated objects <-> TSV)
|
|
31
|
+
- Path integration and identifier files
|
|
32
|
+
- Persistence notes
|
|
33
|
+
- CLI: scout tsv
|
|
34
|
+
- Examples
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Data model and setup
|
|
39
|
+
|
|
40
|
+
A TSV is a Hash-like object extended with Annotation:
|
|
41
|
+
|
|
42
|
+
- key_field — name of the key column.
|
|
43
|
+
- fields — array of field names (data columns).
|
|
44
|
+
- type — shape of values:
|
|
45
|
+
- :double → Array of Arrays per field; row is fields.length arrays.
|
|
46
|
+
- :list → single array (one value per field).
|
|
47
|
+
- :flat → flattened list (space- or sep2-joined values).
|
|
48
|
+
- :single → single scalar value per key.
|
|
49
|
+
- cast — optional value casting (:to_i, :to_f, Proc).
|
|
50
|
+
- filename, namespace, identifiers, serializer — metadata.
|
|
51
|
+
|
|
52
|
+
Construct or annotate:
|
|
53
|
+
- TSV.setup(hash, key_field:, fields:, type:, ...) — extend an existing hash.
|
|
54
|
+
- TSV.setup(array, ...) — converts to a hash-of-arrays with appropriate default.
|
|
55
|
+
|
|
56
|
+
Convenience:
|
|
57
|
+
- TSV.str_setup("ID~FieldA,FieldB#:type=:flat", obj) — parse an option string.
|
|
58
|
+
- TSV.str2options("ID~FieldA,FieldB#:type=:flat") — option parser.
|
|
59
|
+
|
|
60
|
+
Each row’s value is typically a NamedArray bound to fields (unless unnamed true).
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Parsing and opening
|
|
65
|
+
|
|
66
|
+
Open from a file/stream/string with automatic header parsing and options:
|
|
67
|
+
|
|
68
|
+
- TSV.open(file, options = {}) → TSV
|
|
69
|
+
|
|
70
|
+
Header format (optional):
|
|
71
|
+
|
|
72
|
+
- First line (preamble options): "#: key=value#key=value ..."
|
|
73
|
+
- Common keys: :type, :sep, :cast, :merge, :namespace
|
|
74
|
+
- Second line (header): "#Key<sep>Field1<sep>Field2..."
|
|
75
|
+
|
|
76
|
+
Options (selected):
|
|
77
|
+
- sep (default "\t"), sep2 (default "|")
|
|
78
|
+
- type (:double default), merge (:concat supports multi-rows merge)
|
|
79
|
+
- key_field (name or :key)
|
|
80
|
+
- fields ([names] or :all)
|
|
81
|
+
- field: name — shortcut to request only one field (type defaults to :single)
|
|
82
|
+
- grep/invert_grep/fixed_grep — pre-filter raw input via Open.grep
|
|
83
|
+
- tsv_grep — grep by keys in a TSV-aware way
|
|
84
|
+
- head — limit rows
|
|
85
|
+
- cast — :to_i/:to_f/Proc
|
|
86
|
+
- fix — true or Proc (line fixups)
|
|
87
|
+
- persist: true — persistence-backed storage (default engine :HDB via TokyoCabinet)
|
|
88
|
+
- persist_path, persist_prefix, persist_update, data: data cabinet
|
|
89
|
+
- unnamed — when true, row values are plain arrays (no NamedArray)
|
|
90
|
+
- entity_options — used when wrapping values as entities
|
|
91
|
+
- monitor/bar — log/progress control
|
|
92
|
+
|
|
93
|
+
Direct parser:
|
|
94
|
+
- TSV::Parser.new(io_or_path, sep:, header_hash:, type:) — exposes options, key_field, fields, first_line, preamble.
|
|
95
|
+
- TSV.parse(stream_or_parser, ...) — lower-level parse, can write into persistent data stores (data: ScoutCabinet).
|
|
96
|
+
|
|
97
|
+
Detect header/options without full parse:
|
|
98
|
+
- TSV.parse_header(io_or_path) → NamedArray[options, key_field, fields, first_line, preamble, all_fields, namespace]
|
|
99
|
+
- TSV.parse_options(file) → options hash
|
|
100
|
+
|
|
101
|
+
CSV:
|
|
102
|
+
- TSV.csv(obj, headers: true, type: :list, key_field:, fields:, cast:, merge:) — convenience CSV loader.
|
|
103
|
+
|
|
104
|
+
Path helpers:
|
|
105
|
+
- Path#tsv(args) — produce/find and open with TSV.open
|
|
106
|
+
- Path#tsv_options(options) — return parsed header options
|
|
107
|
+
- Path#index(...) — build an index on a TSV path
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Dumper and Transformer
|
|
112
|
+
|
|
113
|
+
Dumper writes TSV streams with preamble and header:
|
|
114
|
+
|
|
115
|
+
- TSV::Dumper.new(key_field:, fields:, type:, sep: "\t", compact:, filename:, namespace:)
|
|
116
|
+
- dumper.init(preamble: true|String)
|
|
117
|
+
- dumper.add(key, value) — value shape depends on dumper.type; supports :double (lists-of-lists) with "|" join.
|
|
118
|
+
- dumper.stream — readable IO (ConcurrentStream-enabled)
|
|
119
|
+
- dumper.tsv — parse its own stream back to a TSV
|
|
120
|
+
- TSV#dumper_stream(into:, preamble:, keys:, unmerge:, stream:) — generate a stream from a TSV (optionally unmerge :double rows into multiple lines).
|
|
121
|
+
|
|
122
|
+
Transformer is a pipeline wrapper that reads from a TSV/Parser and writes via a Dumper:
|
|
123
|
+
|
|
124
|
+
- TSV::Transformer.new(source, dumper=nil, unnamed: nil, namespace: nil)
|
|
125
|
+
- transformer.key_field=, fields=, type=, sep=
|
|
126
|
+
- transformer.traverse { |key, values| [new_key, new_values] } — each block call should return a [key, value] tuple to add.
|
|
127
|
+
- transformer.each { |key, values| ... } — also supports appending via transformer[key] = value.
|
|
128
|
+
- transformer.tsv — finalize and load as TSV.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Traversal and parallelization
|
|
133
|
+
|
|
134
|
+
Uniform traversal API (works for TSV, Hash, Array, IO, Parser and Step):
|
|
135
|
+
|
|
136
|
+
- TSV.traverse(obj, into: nil, cpus: nil, bar: nil, unnamed: true, callback: nil, type:, key_field:, fields:, cast:, select:, one2one:, head:, sep:, ...) { ... }
|
|
137
|
+
- into: :stream | IO | TSV::Dumper | Array | Set | Path | nil
|
|
138
|
+
- cpus: n — parallelize with WorkQueue; your block runs in child processes on serialized arguments.
|
|
139
|
+
- bar: true or ProgressBar options — handy progress indication.
|
|
140
|
+
- Selection: select can be regex/symbol/string/Hash; invert via select: {invert: true}.
|
|
141
|
+
|
|
142
|
+
Examples:
|
|
143
|
+
- TSV.traverse(tsv, into: [], cpus: 2) { |k, v| "#{v[0][0]}-#{Process.pid}" }
|
|
144
|
+
- TSV.traverse(lines, into: :stream) { |line| line.upcase }
|
|
145
|
+
|
|
146
|
+
Header-aware helpers:
|
|
147
|
+
- TSV.process_stream(stream) { |sin, first_non_header_line| ... } — pass-through header lines, handle the payload.
|
|
148
|
+
- TSV.collapse_stream(stream) — preserve headers, collapse body via Open.collapse_stream.
|
|
149
|
+
- TSV#collapse_stream — convenience calling TSV.collapse_stream on self.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Indexing
|
|
154
|
+
|
|
155
|
+
Build mapping indices (value→key or with fields):
|
|
156
|
+
|
|
157
|
+
- TSV.index(tsv_or_path, target:, fields: :all or [names], order: true, persist: true|false, bar:, select:, data: options)
|
|
158
|
+
- Returns a persistence-backed single-type TSV (adapter) mapping any value (in fields) to the first observed target key.
|
|
159
|
+
- With order: true (default), builds a “first seen” mapping across merge.
|
|
160
|
+
- With fields specified, restricts to those fields; fields == :all includes the key itself.
|
|
161
|
+
|
|
162
|
+
Position/range indices (FixWidthTable-backed):
|
|
163
|
+
- TSV.pos_index(tsv, pos_field, key_field: :key, persist:) — point queries (pos or pos range) → list of keys.
|
|
164
|
+
- TSV.range_index(tsv, start_field, end_field, key_field: :key, persist:) — range queries → keys overlapping a pos or range.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Identifier translation and key/field changes
|
|
169
|
+
|
|
170
|
+
Translation across one or more identifier files:
|
|
171
|
+
|
|
172
|
+
- TSV.translation_index(files, source, target, persist: true) → index mapping source → target
|
|
173
|
+
- files may be TSV instances, paths or arrays of both.
|
|
174
|
+
- source can be nil (auto-resolve via path across files).
|
|
175
|
+
- TSV.translate(tsv, field, format, identifiers: nil, one2one:, merge:, stream:, keep:, persist_index:) → translated TSV
|
|
176
|
+
- If field is key_field, changes the key; otherwise changes a column.
|
|
177
|
+
|
|
178
|
+
Change key by column (with identifiers fallback):
|
|
179
|
+
- TSV.change_key(source, new_key_field, identifiers: nil, one2one: false, merge: true, stream: false, keep: false, persist_identifiers: nil)
|
|
180
|
+
|
|
181
|
+
Change an ID column to a different format (attach identifiers and slice):
|
|
182
|
+
- TSV.change_id(source, source_id, new_id, identifiers: nil, one2one: false, insitu: false)
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Attaching tables
|
|
187
|
+
|
|
188
|
+
Join “other” fields onto “source” keyed by matching columns:
|
|
189
|
+
|
|
190
|
+
- TSV.attach(source, other, target: nil|:stream|tsv, fields: nil, index: nil, identifiers: nil, match_key: nil, other_key: nil, one2one: true, complete: false, insitu: (TSV? default true), persist_input: false, bar: nil)
|
|
191
|
+
|
|
192
|
+
Key logic:
|
|
193
|
+
- match_keys picks (match_key, other_key) by fuzzy name matching (NamedArray.field_match), falling back to key fields if needed.
|
|
194
|
+
- If “other” is not a TSV instance, it is opened with key_field=other_key.
|
|
195
|
+
- If keys don’t align directly, build/accept an index via TSV.translation_index(identifiers).
|
|
196
|
+
- Shapes are reconciled (single/list/double/flat) to fit source.type; missing values filled appropriately.
|
|
197
|
+
|
|
198
|
+
Flags:
|
|
199
|
+
- complete: true — add keys present in “other” but missing in “source” (only when match_key == :key).
|
|
200
|
+
- insitu: true — modify source in place (default for TSV); false — produce new object or write into target.
|
|
201
|
+
|
|
202
|
+
Examples:
|
|
203
|
+
- source.attach(other, complete: true)
|
|
204
|
+
- TSV.attach(file1, file2, target: :stream).tsv
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Streaming utilities
|
|
209
|
+
|
|
210
|
+
- TSV.paste_streams(streams, type: :double/:list/:flat, sort: true|false, sort_cmd_args:, sort_memory:, sep:, preamble:, header:, same_fields:, fix_flat:, all_match:, one2one:, field_prefix: [])
|
|
211
|
+
- Aligns multiple TSV streams by key (optionally pre-sorted), merges their columns and emits a single stream with unioned fields.
|
|
212
|
+
- TSV.concat_streams(streams)
|
|
213
|
+
- Concatenate multiple TSV streams preserving the first header and merging rows by key when read with merge: true.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## CSV utilities
|
|
218
|
+
|
|
219
|
+
- TSV.csv(obj, headers: true, type: :list, key_field:, fields:)
|
|
220
|
+
- Load CSV-like content from String/IO/Path.
|
|
221
|
+
- If key_field or fields are provided, returns a properly shaped TSV (reorders or converts type accordingly).
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Column and row utilities
|
|
226
|
+
|
|
227
|
+
Process and add new columns:
|
|
228
|
+
- TSV#process(field) { |field_values| ... } or { |field_values, key| ... } or { |field_values, key, row_values| ... } → self
|
|
229
|
+
- TSV#add_field(name = nil) { |key, row_values| new_values } → append a column.
|
|
230
|
+
|
|
231
|
+
De-duplicate:
|
|
232
|
+
- TSV#remove_duplicates(pivot = 0) → new TSV removing duplicate zipped rows (keeps unique element-wise tuples).
|
|
233
|
+
|
|
234
|
+
Melt columns:
|
|
235
|
+
- TSV#melt_columns(value_field, column_field) → long-form table with fields [key_field, value_field, column_field]
|
|
236
|
+
|
|
237
|
+
Selecting rows:
|
|
238
|
+
- TSV.select(key, values, criterion, fields:, field:, invert:, type:, sep:) → boolean (low-level)
|
|
239
|
+
- TSV#select(method=nil, invert=false) { |k, v| ... } → new TSV:
|
|
240
|
+
- method can be: Array (membership), Regexp, String/Symbol, Hash (field => criterion), Numeric, Proc
|
|
241
|
+
- In Hash form, :key targets keys; "name:..." supports entity name matching.
|
|
242
|
+
- In String form ">=5" etc. for numeric thresholds.
|
|
243
|
+
|
|
244
|
+
Subsets and chunking:
|
|
245
|
+
- TSV#subset(keys)
|
|
246
|
+
- TSV#chunked_values_at(keys, max = 5000) → batched lookup lists (ordered).
|
|
247
|
+
|
|
248
|
+
Reorder and slicing:
|
|
249
|
+
- TSV#reorder(key_field=nil, fields=nil, merge: true, one2one: true, data: nil, unnamed: true, type:) → new TSV with new key and/or field selection.
|
|
250
|
+
- TSV#slice(fields) → shortcut to reorder by same key.
|
|
251
|
+
- TSV#column(field, cast:) → new single/flat column TSV.
|
|
252
|
+
|
|
253
|
+
Transpositions:
|
|
254
|
+
- TSV#transpose(key_field="Unknown ID") → transpose by rows/columns, shape-preserving:
|
|
255
|
+
- list/double variants provide transpose_list/transpose_double.
|
|
256
|
+
- TSV#head(max=10) → first n rows.
|
|
257
|
+
|
|
258
|
+
Sorting and paging:
|
|
259
|
+
- TSV#sort_by(field=:all, just_keys=false) { |(k, v)| ... } — returns sorted [key, value] pairs or just keys.
|
|
260
|
+
- TSV#sort(field=:all, just_keys=false) { |a, b| ... }
|
|
261
|
+
- TSV#page(pnum, psize, field=nil, just_keys=false, reverse=false) — returns page slice or keys.
|
|
262
|
+
|
|
263
|
+
Unzip/zip:
|
|
264
|
+
- TSV#unzip(field, target: nil|:stream|tsv, sep: ":", delete: true, type: :list, merge: false, one2one: true, bar: nil)
|
|
265
|
+
- Split each row by a field’s values, emitting new keys key+sep+field_value.
|
|
266
|
+
- delete: removes that column when forming new rows.
|
|
267
|
+
- merge: combine replicates by new key (deduplicate values).
|
|
268
|
+
- TSV#unzip_replicates — expand :double replicates into separate pseudo-rows (key(i)).
|
|
269
|
+
- TSV#zip(merge=false, field="New Field", sep: ":") — reverse after unzip (merge replicates optionally).
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Filters
|
|
274
|
+
|
|
275
|
+
Add persisted or in-memory filters affecting iteration transparently:
|
|
276
|
+
|
|
277
|
+
- tsv.filter(filter_dir=nil) — extend with Filtered; set filter_dir for persistent filters.
|
|
278
|
+
- tsv.add_filter(match, value, persistence=nil) — filter rows
|
|
279
|
+
- match: "field:FieldName" to match on a field; :key to match on keys.
|
|
280
|
+
- value: String or Array; stored as Set when needed.
|
|
281
|
+
- persistence: Hash or HDB path for persistent cache of ids.
|
|
282
|
+
- tsv.pop_filter — remove last filter (finalize pending updates).
|
|
283
|
+
- tsv.reset_filters — delete all saved filters.
|
|
284
|
+
|
|
285
|
+
When filters are present:
|
|
286
|
+
- filename and keys/values/each/collect are transparently scoped to filtered ids.
|
|
287
|
+
- size returns filtered size.
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Annotation integration
|
|
292
|
+
|
|
293
|
+
Serialize annotated objects to TSV:
|
|
294
|
+
|
|
295
|
+
- Annotation.tsv(objs, *fields)
|
|
296
|
+
- objs can be a single annotated object, an array of annotated objects, or annotated arrays.
|
|
297
|
+
- fields defaults to all annotations + :annotation_types (or :all to include :literal).
|
|
298
|
+
- Produces a TSV with key_field nil or special keys ("List" or object ids).
|
|
299
|
+
- Annotation.load_tsv(tsv) — reconstruct annotated objects from TSV.
|
|
300
|
+
|
|
301
|
+
Persist annotated objects into a repo (Tokyo Cabinet):
|
|
302
|
+
- Persist.annotation_repo_persist(repo_or_path, name) { annotations }
|
|
303
|
+
- Stores or retrieves annotations by a name subkey; supports nil/empty, single objects, arrays, and annotated double arrays.
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## Path integration and identifier files
|
|
308
|
+
|
|
309
|
+
- A TSV loaded from a Path gets filename set to that Path (and Path metadata).
|
|
310
|
+
- If a sibling “identifiers” file is present, it is auto-loaded into tsv.identifiers.
|
|
311
|
+
- Path#identifier_file_path conveniently locates such files.
|
|
312
|
+
|
|
313
|
+
Utilities:
|
|
314
|
+
- TSV#identifier_files — discover identifier TSVs from:
|
|
315
|
+
- tsv.identifiers (if set),
|
|
316
|
+
- tsv.filename.dirname.identifiers, or options.
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Persistence notes
|
|
321
|
+
|
|
322
|
+
- TSV.open with persist: true returns a persistence-backed TSV (ScoutCabinet/TokyoCabinet HDB by default). You can reopen the cabinet and still see options (key_field/fields/type).
|
|
323
|
+
- TSV.parse can directly stream data into persistence when data.responds_to?(:load_stream) and conditions allow (same type, no transformations) — faster load.
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Command Line Interface (scout tsv)
|
|
328
|
+
|
|
329
|
+
The scout command discovers TSV-related scripts under scout_commands/tsv across installed packages using the Path subsystem. Usage pattern:
|
|
330
|
+
|
|
331
|
+
- Listing and discovery:
|
|
332
|
+
- scout tsv
|
|
333
|
+
- If you specify a category (directory) rather than a script, a list of available subcommands is shown.
|
|
334
|
+
- Running a specific TSV subcommand:
|
|
335
|
+
- scout tsv <subcommand> [options] [args...]
|
|
336
|
+
- The dispatcher resolves to scout_commands/tsv/<subcommand> and executes it.
|
|
337
|
+
- Remaining ARGV is parsed using SOPT (SimpleOPT) as specified by the subcommand.
|
|
338
|
+
- Nested commands:
|
|
339
|
+
- Subcommands may themselves be directories; e.g., scout tsv attach ... would run scout_commands/tsv/attach if present, or list subcommands under attach if attach is a directory.
|
|
340
|
+
|
|
341
|
+
Script availability depends on installed TSV utilities in your environment (frameworks and workflows can register their own under share/scout_commands/tsv). Typical operations shipped by packages include attaching files, translating identifiers, building indexes, pasting/concatenating streams, and other TSV manipulations. Use scout tsv to explore installed commands.
|
|
342
|
+
|
|
343
|
+
Note: If the selected path is a directory, a help-like listing is printed. The bin/scout resolver uses Path to locate scripts from all installed packages.
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Examples
|
|
348
|
+
|
|
349
|
+
Open and traverse:
|
|
350
|
+
|
|
351
|
+
```ruby
|
|
352
|
+
content = <<~EOF
|
|
353
|
+
#: :sep=" " #:type=:double
|
|
354
|
+
#Id ValueA ValueB
|
|
355
|
+
row1 a|aa b|bb
|
|
356
|
+
row2 A B
|
|
357
|
+
EOF
|
|
358
|
+
|
|
359
|
+
tsv = TSV.open(StringIO.new(content))
|
|
360
|
+
tsv.unnamed = false
|
|
361
|
+
tsv.each do |k, v|
|
|
362
|
+
puts [k, v["ValueA"]].inspect
|
|
363
|
+
end
|
|
364
|
+
# => ["row1", ["a","aa"]]
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Attach:
|
|
368
|
+
|
|
369
|
+
```ruby
|
|
370
|
+
a = TSV.open <<~A
|
|
371
|
+
#: :sep=" "
|
|
372
|
+
#Id ValueA
|
|
373
|
+
row1 a
|
|
374
|
+
row2 A
|
|
375
|
+
A
|
|
376
|
+
|
|
377
|
+
b = TSV.open <<~B
|
|
378
|
+
#: :sep=" "
|
|
379
|
+
#Id Other
|
|
380
|
+
row1 X
|
|
381
|
+
row3 Y
|
|
382
|
+
B
|
|
383
|
+
|
|
384
|
+
a.attach(b, complete: true)
|
|
385
|
+
a["row1"]["Other"] # => ["X"]
|
|
386
|
+
a["row3"]["Other"] # => ["Y"] (added by complete)
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
Change key and translate:
|
|
390
|
+
|
|
391
|
+
```ruby
|
|
392
|
+
# tsv with identifiers mapping ValueA -> X
|
|
393
|
+
tsv = TSV.open(tf1, identifiers: ti)
|
|
394
|
+
tsv2 = TSV.change_key(tsv, "X")
|
|
395
|
+
tsv2.include?("x") # => true
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
Build an index:
|
|
399
|
+
|
|
400
|
+
```ruby
|
|
401
|
+
index = TSV.index(tsv, target: "ValueB")
|
|
402
|
+
index["a"] # => "b"
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
Paste streams:
|
|
406
|
+
|
|
407
|
+
```ruby
|
|
408
|
+
s1 = StringIO.new "#Row A\nr1 1\nr2 2\n".gsub(" ", "\t")
|
|
409
|
+
s2 = StringIO.new "#Row B\nr1 10\nr2 20\n".gsub(" ", "\t")
|
|
410
|
+
out = TSV.paste_streams([s1, s2], sort: true, type: :list)
|
|
411
|
+
TSV.open(out)["r2"] # => ["2","20"]
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
Unzip/zip:
|
|
415
|
+
|
|
416
|
+
```ruby
|
|
417
|
+
unzipped = tsv.unzip("ValueA", delete: true)
|
|
418
|
+
unzipped["row1:a"]["ValueB"] # => "b" (or ["b"] depending on type)
|
|
419
|
+
rezipped = unzipped.zip(true) # merge back by base key
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
Filters:
|
|
423
|
+
|
|
424
|
+
```ruby
|
|
425
|
+
tsv.filter
|
|
426
|
+
tsv.add_filter "field:ValueA", ["A"]
|
|
427
|
+
tsv.keys # filtered keys only
|
|
428
|
+
tsv.pop_filter
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
CSV:
|
|
432
|
+
|
|
433
|
+
```ruby
|
|
434
|
+
tsv = TSV.csv("Key,FieldA\nk1,a\n", headers: true, type: :list)
|
|
435
|
+
tsv["k1"]["FieldA"] # => "a"
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
Annotation TSV:
|
|
439
|
+
|
|
440
|
+
```ruby
|
|
441
|
+
module A; extend Annotation; annotation :code; end
|
|
442
|
+
str = A.setup("s1", code: "C")
|
|
443
|
+
tsv = Annotation.tsv([str], :all)
|
|
444
|
+
list = Annotation.load_tsv(tsv) # => ["s1"] annotated back
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
449
|
+
TSV unifies table processing with streaming, persistence, and composable transforms, while interoperating with the rest of the Scout stack (Open, WorkQueue, Persist, Path, Annotation, and Entity). Use TSV.open/parse for ingestion, Dumper/Transformer for streaming pipelines, traverse for parallel computation, and the utility methods for reshaping and joining datasets. For CLI interaction, explore scout tsv subcommands provided by installed packages.
|