scout-essentials 1.8.8 → 1.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 +26 -12
- data/README.md +83 -112
- data/VERSION +1 -1
- data/doc/Improvements.md +226 -0
- data/doc/StartHere.md +122 -0
- data/doc/developer/AnnotationSystem.md +184 -0
- data/doc/developer/Architecture.md +147 -0
- data/doc/developer/Configuration.md +238 -0
- data/doc/developer/CoreUtilities.md +265 -0
- data/doc/developer/DesignPrinciples.md +129 -0
- data/doc/developer/ErrorHandling.md +203 -0
- data/doc/developer/LockingAndConcurrency.md +157 -0
- data/doc/developer/PathResolution.md +200 -0
- data/doc/developer/PersistenceAndResources.md +119 -0
- data/doc/developer/StreamingModel.md +236 -0
- data/doc/user/AnnotatingData.md +202 -0
- data/doc/user/CachingResults.md +183 -0
- data/doc/user/CommandLineOptions.md +189 -0
- data/doc/user/Cookbook.md +211 -0
- data/doc/user/HandlingStreams.md +236 -0
- data/doc/user/LoggingAndProgress.md +158 -0
- data/doc/user/ProducingResources.md +177 -0
- data/doc/user/RemoteData.md +157 -0
- data/doc/user/RunningCommands.md +218 -0
- data/doc/user/WorkingWithFiles.md +217 -0
- data/lib/scout/cmd.rb +343 -40
- data/lib/scout/concurrent_stream.rb +14 -1
- data/lib/scout/indiferent_hash.rb +1 -1
- data/lib/scout/log/fingerprint.rb +13 -8
- data/lib/scout/log/progress/report.rb +1 -1
- data/lib/scout/log.rb +4 -1
- data/lib/scout/misc/format.rb +24 -0
- data/lib/scout/named_array.rb +1 -1
- data/lib/scout/open/stream.rb +2 -2
- data/lib/scout/open/util.rb +4 -0
- data/lib/scout/path/find.rb +3 -2
- data/lib/scout/persist.rb +14 -10
- data/research/annotations-data-analysis.md +206 -0
- data/research/behavior-probes.md +1925 -0
- data/research/commands-streaming-analysis.md +272 -0
- data/research/design-philosophy-analysis.md +383 -0
- data/research/doc-audit-findings.md +294 -0
- data/research/ecosystem-attribution.md +118 -0
- data/research/implementation-inventory-core.md +1029 -0
- data/research/implementation-inventory-open.md +417 -0
- data/research/implementation-inventory-path-persist-resource.md +774 -0
- data/research/io-paths-analysis.md +228 -0
- data/research/persistence-resources-analysis.md +244 -0
- data/research/synthesis-report.md +80 -0
- data/scout-essentials.gemspec +37 -15
- data/test/scout/test_cmd.rb +411 -0
- metadata +36 -14
- data/doc/Annotation.md +0 -352
- data/doc/CMD.md +0 -363
- data/doc/ConcurrentStream.md +0 -163
- data/doc/IndiferentHash.md +0 -240
- data/doc/Log.md +0 -235
- data/doc/NamedArray.md +0 -174
- data/doc/Open.md +0 -331
- data/doc/Path.md +0 -217
- data/doc/Persist.md +0 -214
- data/doc/Resource.md +0 -229
- data/doc/SimpleOPT.md +0 -236
- data/doc/TmpFile.md +0 -154
data/lib/scout/path/find.rb
CHANGED
|
@@ -21,7 +21,7 @@ module Path
|
|
|
21
21
|
file
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
def self.caller_lib_dir(file = nil, relative_to = ['lib', 'bin'])
|
|
24
|
+
def self.caller_lib_dir(file = nil, relative_to = ['lib', 'bin', 'README.md'])
|
|
25
25
|
|
|
26
26
|
file = caller_file(file)
|
|
27
27
|
|
|
@@ -85,6 +85,7 @@ module Path
|
|
|
85
85
|
def self.path_maps
|
|
86
86
|
@@path_maps ||= IndiferentHash.setup({
|
|
87
87
|
:current => "{PWD}/{TOPLEVEL}/{SUBPATH}",
|
|
88
|
+
:home => "{HOME}/{TOPLEVEL}/{PKGDIR}/{SUBPATH}",
|
|
88
89
|
:user => "{HOME}/.{PKGDIR}/{TOPLEVEL}/{SUBPATH}",
|
|
89
90
|
:global => '/{TOPLEVEL}/{PKGDIR}/{SUBPATH}',
|
|
90
91
|
:usr => '/usr/{TOPLEVEL}/{PKGDIR}/{SUBPATH}',
|
|
@@ -100,7 +101,7 @@ module Path
|
|
|
100
101
|
end
|
|
101
102
|
|
|
102
103
|
def self.basic_map_order
|
|
103
|
-
@@basic_map_order ||= %w(current workflow user local global usr lib fast cache bulk).collect{|m| m.to_sym }
|
|
104
|
+
@@basic_map_order ||= %w(current workflow user home local global usr lib fast cache bulk).collect{|m| m.to_sym }
|
|
104
105
|
end
|
|
105
106
|
|
|
106
107
|
def self.map_order
|
data/lib/scout/persist.rb
CHANGED
|
@@ -118,16 +118,20 @@ module Persist
|
|
|
118
118
|
pres = Persist.save(res, file, type)
|
|
119
119
|
res = pres unless pres.nil?
|
|
120
120
|
end
|
|
121
|
-
rescue Exception
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
121
|
+
rescue Exception => e
|
|
122
|
+
begin
|
|
123
|
+
Thread.handle_interrupt(Exception => :never) do
|
|
124
|
+
if Open.exist?(file)
|
|
125
|
+
Log.debug "Failed persistence #{file} - erasing"
|
|
126
|
+
Open.rm_rf file
|
|
127
|
+
else
|
|
128
|
+
Log.debug "Failed persistence #{file}"
|
|
129
|
+
end
|
|
130
|
+
end unless DontPersist === e
|
|
131
|
+
rescue
|
|
132
|
+
ensure
|
|
133
|
+
raise e unless options[:canfail]
|
|
134
|
+
end
|
|
131
135
|
end
|
|
132
136
|
|
|
133
137
|
if TrueClass === no_load
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Investigation: Annotations, Data Structures, and Options
|
|
2
|
+
|
|
3
|
+
**Status:** Non-normative investigation artifact. May be outdated.
|
|
4
|
+
|
|
5
|
+
## Scope
|
|
6
|
+
Annotation, NamedArray, IndiferentHash, CaseInsensitiveHash, SimpleOPT (SOPT).
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Annotation system
|
|
11
|
+
|
|
12
|
+
### What it is
|
|
13
|
+
A lightweight, non-invasive system for attaching typed, named metadata
|
|
14
|
+
(instance variables with accessors) to **any** Ruby object — strings, arrays,
|
|
15
|
+
hashes — without subclassing or wrapping. The object's class never changes.
|
|
16
|
+
|
|
17
|
+
### Core mechanism
|
|
18
|
+
1. A module calls `extend Annotation` to become an "annotation module."
|
|
19
|
+
2. `extend Annotation` triggers `Annotation.extended(base)`, which:
|
|
20
|
+
- Initializes `@annotations = []` on the module.
|
|
21
|
+
- `include`s `AnnotatedObject` into the module (providing `annotate`,
|
|
22
|
+
`purge`, `annotation_hash`, etc.).
|
|
23
|
+
- `extend`s `AnnotationModule` onto the module (providing `annotation`,
|
|
24
|
+
`setup`, `annotations`).
|
|
25
|
+
3. `annotation :attr1, :attr2` calls `attr_accessor` on each and records them
|
|
26
|
+
in `@annotations`.
|
|
27
|
+
4. `MyMod.setup(obj, ...)` extends `obj` with `MyMod`, copies annotation types
|
|
28
|
+
into `obj.annotation_types`, and sets instance variables for each annotation
|
|
29
|
+
attribute.
|
|
30
|
+
|
|
31
|
+
### setup() parameter resolution
|
|
32
|
+
`setup` is flexible about arguments:
|
|
33
|
+
- Positional: `MyMod.setup(obj, val1, val2)` → `@attr1 = val1, @attr2 = val2`
|
|
34
|
+
(via `attrs.zip(rest)`).
|
|
35
|
+
- Hash: `MyMod.setup(obj, {attr1: val1, attr2: val2})` → used directly as pairs
|
|
36
|
+
when keys match annotation names.
|
|
37
|
+
- Block: `setup(*args) { |obj| ... }` — block receives the object, args are rest.
|
|
38
|
+
- Frozen objects are dup'd before extending.
|
|
39
|
+
|
|
40
|
+
### AnnotatedObject mixin
|
|
41
|
+
Provides:
|
|
42
|
+
- `annotation_types` — array of modules applied to this object.
|
|
43
|
+
- `base_type` — last annotation type applied (the "primary" type).
|
|
44
|
+
- `annotation_hash` — `{ name => value }` for all annotated attributes.
|
|
45
|
+
- `annotate(other)` — copies all annotations to another object.
|
|
46
|
+
- `purge` — returns a clean duplicate with all annotation ivars removed.
|
|
47
|
+
- `annotation_id` / `id` — digest of `[self, annotation_info]`.
|
|
48
|
+
- `make_array` — wraps self in a single-element array and extends with AnnotatedArray.
|
|
49
|
+
|
|
50
|
+
### AnnotatedArray mixin
|
|
51
|
+
When an annotated object is itself an Array, `AnnotatedArray` provides:
|
|
52
|
+
- Automatic annotation propagation: `[]`, `first`, `last`, `each`, `each_with_index`,
|
|
53
|
+
`select`, `collect`, `compact`, `uniq`, `flatten`, `reverse`, `sort_by` —
|
|
54
|
+
each returns results annotated with the parent's types.
|
|
55
|
+
- `annotate_item(obj, pos)` — sets `container` and `container_index` on each
|
|
56
|
+
item (via `AnnotatedArrayItem`), then annotates.
|
|
57
|
+
- `subset(list)`, `remove(list)` — set operations that preserve annotations.
|
|
58
|
+
|
|
59
|
+
### Key insight: the propagate-through-enumeration pattern
|
|
60
|
+
AnnotatedArray overrides Array enumeration methods so that **every element
|
|
61
|
+
extracted from an annotated array inherits the parent's annotations**. This
|
|
62
|
+
is why you can do:
|
|
63
|
+
```ruby
|
|
64
|
+
list = Gene.setup(["BRCA1", "TP53"], :organism, "Hsa")
|
|
65
|
+
list.first.organism # => "Hsa" (inherited from parent)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## NamedArray
|
|
71
|
+
|
|
72
|
+
### What it is
|
|
73
|
+
An extension of Annotation for Arrays that adds **named fields** and name-based
|
|
74
|
+
accessors. Built directly on the Annotation system.
|
|
75
|
+
|
|
76
|
+
### Annotations
|
|
77
|
+
- `fields` — ordered list of field names for each array position.
|
|
78
|
+
- `key` — optional primary key field name.
|
|
79
|
+
|
|
80
|
+
### Key methods
|
|
81
|
+
- `[](name_or_index)` — resolves a name to position via `identify_name`, then
|
|
82
|
+
delegates to `Array#[]`.
|
|
83
|
+
- `[]=(name_or_index, value)` — same resolution for assignment.
|
|
84
|
+
- `to_hash` — returns an `IndiferentHash` mapping field names → values.
|
|
85
|
+
- `concat(hash_or_array)` — if given a Hash, appends its values and adds keys
|
|
86
|
+
to fields.
|
|
87
|
+
- `method_missing` — provides getter accessors for field names (`a.foo`).
|
|
88
|
+
|
|
89
|
+
### Name resolution (identify_name)
|
|
90
|
+
- `nil` → 0
|
|
91
|
+
- Integer/Range → returned as-is
|
|
92
|
+
- Symbol `:key` → returns `:key` sentinel
|
|
93
|
+
- Otherwise: exact match, then numeric-as-index, then fuzzy match
|
|
94
|
+
(parentheses containment, space-prefix).
|
|
95
|
+
- `strict: true` disables fuzzy matching.
|
|
96
|
+
|
|
97
|
+
### Class helpers
|
|
98
|
+
- `zip_fields(array)` — transpose a list-of-lists.
|
|
99
|
+
- `add_zipped(source, new)` — incrementally merge zipped lists.
|
|
100
|
+
- `field_match(field, name)` — fuzzy matching predicate.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## IndiferentHash
|
|
105
|
+
|
|
106
|
+
### What it is
|
|
107
|
+
A module mixin for Hash instances that makes key access indifferent to
|
|
108
|
+
String vs Symbol. Also includes a rich set of options-processing utilities.
|
|
109
|
+
|
|
110
|
+
### Core behavior
|
|
111
|
+
- `setup(hash)` extends a single hash instance.
|
|
112
|
+
- `[](key)` — tries exact key, then alternate form (symbol ↔ string).
|
|
113
|
+
- `[]=(key, value)` — deletes any existing variant first, then sets.
|
|
114
|
+
- `include?(key)` — checks both forms.
|
|
115
|
+
- Nested hashes are auto-extended on read.
|
|
116
|
+
- `merge`, `deep_merge`, `slice`, `except`, `values_at`, `delete` — all
|
|
117
|
+
form-indifferent.
|
|
118
|
+
- `clean_version` — returns a plain Hash with stringified keys.
|
|
119
|
+
|
|
120
|
+
### Options utilities (IndiferentHash::Options)
|
|
121
|
+
- `add_defaults(options, defaults)` — adds missing keys.
|
|
122
|
+
- `process_options(hash, *keys)` — extract-and-remove keys, with defaults.
|
|
123
|
+
- `pull_keys(hash, prefix)` — extract `prefix_*` keys into a new hash.
|
|
124
|
+
- `zip2hash(list1, list2)` — zip two lists into an IndiferentHash.
|
|
125
|
+
- `positional2hash(keys, *values)` — convert positional args to a hash.
|
|
126
|
+
- `hash2string` / `string2hash` — serialize/parse simple hashes.
|
|
127
|
+
- `parse_options(str)` — parse shell-like `key=value` strings.
|
|
128
|
+
- `print_options(options)` — serialize to space-separated string.
|
|
129
|
+
|
|
130
|
+
### CaseInsensitiveHash
|
|
131
|
+
Separate mixin: `setup(hash)` makes string key lookup case-insensitive
|
|
132
|
+
by maintaining a `original_key_by_downcase` map.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## SimpleOPT (SOPT)
|
|
137
|
+
|
|
138
|
+
### What it is
|
|
139
|
+
A lightweight command-line option parser with:
|
|
140
|
+
- A compact DSL for declaring options.
|
|
141
|
+
- `--long` / `-short` / `--key=value` parsing.
|
|
142
|
+
- Boolean and string-typed options.
|
|
143
|
+
- Usage/help text generation.
|
|
144
|
+
|
|
145
|
+
### Declaration styles
|
|
146
|
+
1. `SOPT.parse("-f--first* first arg")` — compact definition string.
|
|
147
|
+
2. `SOPT.register(short, long, asterisk, description)` — explicit.
|
|
148
|
+
3. `SOPT.setup(heredoc)` — parse a help-text heredoc, register options,
|
|
149
|
+
and auto-consume ARGV.
|
|
150
|
+
|
|
151
|
+
### Option format
|
|
152
|
+
`-short--long[*] description`
|
|
153
|
+
- `*` marks string-valued; absence marks boolean.
|
|
154
|
+
- Short is optional (auto-generated if nil/true).
|
|
155
|
+
|
|
156
|
+
### Parsing (consume)
|
|
157
|
+
- `SOPT.consume(args = ARGV)` scans tokens, removes recognized options.
|
|
158
|
+
- Returns an IndiferentHash with symbol keys.
|
|
159
|
+
- Accumulates into `SOPT.GOT_OPTIONS`.
|
|
160
|
+
|
|
161
|
+
### Documentation generation
|
|
162
|
+
- `SOPT.doc` — full manpage-style help.
|
|
163
|
+
- `SOPT.input_format` — single-option usage fragment.
|
|
164
|
+
- `SOPT.usage` — print doc and exit.
|
|
165
|
+
|
|
166
|
+
### Gotchas
|
|
167
|
+
- `input_defaults` is **documentation-only**; parser does not apply defaults.
|
|
168
|
+
- Shortcut auto-generation skips punctuation (`.`, `-`, `_`).
|
|
169
|
+
- Boolean false must be `--flag=false` or a `false`/`F`/`no` token.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Cross-module interactions
|
|
174
|
+
|
|
175
|
+
- **NamedArray depends on Annotation** — extends Annotation, declares
|
|
176
|
+
`:fields` and `:key`.
|
|
177
|
+
- **NamedArray.to_hash depends on IndiferentHash** — returns IndiferentHash.
|
|
178
|
+
- **SOPT depends on IndiferentHash** — parsed options are IndiferentHash;
|
|
179
|
+
`parse_options` and `print_options` from IndiferentHash::Options are used.
|
|
180
|
+
- **SOPT uses Log** — for colored output in doc generation.
|
|
181
|
+
- **Annotation.purge recursively purges** — handles nested Arrays and Hashes.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Gotchas and warnings
|
|
186
|
+
|
|
187
|
+
1. **Annotation.setup with a single Hash arg and multiple attributes**: the
|
|
188
|
+
condition `attrs.length != 1` is buggy — it should be `attrs.length == 1`
|
|
189
|
+
(line in annotation_module.rb). This can cause unexpected behavior when
|
|
190
|
+
setting up with a hash where keys don't match attribute names.
|
|
191
|
+
2. **IndiferentHash#[] with default/default_proc**: if the hash has a default
|
|
192
|
+
and the key is not in `keys`, the default is returned **without** trying
|
|
193
|
+
the alternate form. This can silently mask symbol/string mismatches.
|
|
194
|
+
3. **SOPT fix_shortcut may return nil** — if no unique shortcut can be found,
|
|
195
|
+
the option is registered with no shortcut. No error is raised.
|
|
196
|
+
4. **AnnotatedArray collect vs map**: `collect` is overridden but `map` is
|
|
197
|
+
not explicitly handled. In Ruby `map` is an alias for `collect`, so it
|
|
198
|
+
should work, but the override uses `inject` rather than `super`, which
|
|
199
|
+
loses any block-passing nuance.
|
|
200
|
+
5. **NamedArray#method_missing provides getters only** — no setters. Use `[]=`.
|
|
201
|
+
6. **IndiferentHash#keys_to_sym!** uses `rescue` for failed conversions,
|
|
202
|
+
silently skipping unconvertible keys.
|
|
203
|
+
7. **Annotation on frozen objects**: setup dup's frozen objects, but callers
|
|
204
|
+
may not expect the returned object to be a different instance.
|
|
205
|
+
8. **Pretty print misspelling**: the method is `prety_print` (one 't') in
|
|
206
|
+
some places — check carefully.
|