scout-gear 10.8.3 → 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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.vimproject +17 -0
  3. data/README.md +352 -0
  4. data/Rakefile +1 -0
  5. data/VERSION +1 -1
  6. data/doc/Association.md +288 -0
  7. data/doc/Entity.md +296 -0
  8. data/doc/KnowledgeBase.md +433 -0
  9. data/doc/Persist.md +356 -0
  10. data/doc/Semaphore.md +171 -0
  11. data/doc/TSV.md +449 -0
  12. data/doc/WorkQueue.md +359 -0
  13. data/doc/Workflow.md +586 -0
  14. data/lib/scout/association.rb +4 -2
  15. data/lib/scout/entity/identifiers.rb +1 -1
  16. data/lib/scout/entity/object.rb +1 -1
  17. data/lib/scout/entity/property.rb +5 -5
  18. data/lib/scout/entity.rb +1 -1
  19. data/lib/scout/knowledge_base/description.rb +1 -1
  20. data/lib/scout/knowledge_base/list.rb +7 -2
  21. data/lib/scout/knowledge_base/registry.rb +2 -2
  22. data/lib/scout/knowledge_base.rb +20 -2
  23. data/lib/scout/monitor.rb +300 -0
  24. data/lib/scout/persist/engine/packed_index.rb +2 -2
  25. data/lib/scout/persist/engine/sharder.rb +1 -1
  26. data/lib/scout/persist/tsv.rb +1 -0
  27. data/lib/scout/semaphore.rb +1 -1
  28. data/lib/scout/tsv/dumper.rb +3 -3
  29. data/lib/scout/tsv/open.rb +1 -0
  30. data/lib/scout/tsv/parser.rb +1 -1
  31. data/lib/scout/tsv/transformer.rb +1 -0
  32. data/lib/scout/tsv/util.rb +2 -2
  33. data/lib/scout/work_queue/socket.rb +1 -1
  34. data/lib/scout/work_queue/worker.rb +7 -5
  35. data/lib/scout/workflow/documentation.rb +1 -1
  36. data/lib/scout/workflow/entity.rb +22 -1
  37. data/lib/scout/workflow/step/config.rb +3 -3
  38. data/lib/scout/workflow/step/file.rb +4 -0
  39. data/lib/scout/workflow/step/info.rb +8 -2
  40. data/lib/scout/workflow/step.rb +10 -5
  41. data/lib/scout/workflow/task/inputs.rb +1 -1
  42. data/lib/scout/workflow/usage.rb +3 -2
  43. data/lib/scout/workflow/util.rb +22 -0
  44. data/scout-gear.gemspec +20 -6
  45. data/scout_commands/cat +86 -0
  46. data/scout_commands/doc +3 -1
  47. data/scout_commands/entity +151 -0
  48. data/scout_commands/system/clean +146 -0
  49. data/scout_commands/system/status +238 -0
  50. data/scout_commands/workflow/info +23 -10
  51. data/scout_commands/workflow/install +1 -1
  52. data/scout_commands/workflow/task +1 -1
  53. data/test/scout/entity/test_property.rb +1 -1
  54. data/test/scout/knowledge_base/test_registry.rb +19 -0
  55. data/test/scout/test_work_queue.rb +1 -1
  56. data/test/scout/work_queue/test_worker.rb +12 -10
  57. metadata +32 -5
  58. data/doc/lib/scout/path.md +0 -35
  59. 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.