scout-essentials 1.8.7 → 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/digest.rb +6 -5
- 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 +8 -4
- data/lib/scout/open.rb +3 -3
- data/lib/scout/path/find.rb +3 -2
- data/lib/scout/persist.rb +14 -10
- data/lib/scout/resource/produce.rb +9 -1
- 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/open/test_remote.rb +1 -2
- 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/doc/Annotation.md
DELETED
|
@@ -1,352 +0,0 @@
|
|
|
1
|
-
# Annotation
|
|
2
|
-
|
|
3
|
-
The Annotation module provides a lightweight system for adding typed, named "annotations" (simple instance variables with accessors) to arbitrary Ruby objects and arrays. It's used by defining annotation modules (modules that `extend Annotation` and declare attributes via `annotation`) and then applying those annotation modules to objects at runtime.
|
|
4
|
-
|
|
5
|
-
Key features:
|
|
6
|
-
- Define annotation modules with named attributes.
|
|
7
|
-
- Attach annotation modules to objects or arrays (including Strings, Arrays, Hashes, Procs, etc.).
|
|
8
|
-
- Annotated arrays (`AnnotatedArray`) propagate annotations to their items and provide annotation-aware iteration and collection operations.
|
|
9
|
-
- Support for serialization (Marshal) and a `purge` operation that strips annotations from objects (recursively for arrays and hashes).
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## Creating an annotation module
|
|
14
|
-
|
|
15
|
-
Define a module and `extend Annotation`. Declare annotation attributes using `annotation :attr1, :attr2`.
|
|
16
|
-
|
|
17
|
-
Example:
|
|
18
|
-
|
|
19
|
-
```ruby
|
|
20
|
-
module MyAnnotation
|
|
21
|
-
extend Annotation
|
|
22
|
-
annotation :code, :note
|
|
23
|
-
end
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
When a module is created this way, it gets:
|
|
27
|
-
- an internal `@annotations` list (names of attributes),
|
|
28
|
-
- accessors for declared attributes (e.g. `code`, `code=`),
|
|
29
|
-
- the ability to be used to annotate objects (`MyAnnotation.setup(obj, ...)` or by `obj.extend MyAnnotation`).
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## Applying annotations
|
|
34
|
-
|
|
35
|
-
Use `Annotation.setup` or the annotation module's `setup` to attach annotations to objects.
|
|
36
|
-
|
|
37
|
-
Basic usage:
|
|
38
|
-
|
|
39
|
-
- Annotation.setup with a single annotation module:
|
|
40
|
-
```ruby
|
|
41
|
-
Annotation.setup(obj, MyAnnotation, code: "X")
|
|
42
|
-
```
|
|
43
|
-
or
|
|
44
|
-
```ruby
|
|
45
|
-
MyAnnotation.setup(obj, code: "X")
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
- Annotation.setup accepts annotation types as:
|
|
49
|
-
- a Module constant (e.g. `MyAnnotation`),
|
|
50
|
-
- an Array of modules (`[A, B]`),
|
|
51
|
-
- a String of module names separated by `|` (will be constantized).
|
|
52
|
-
|
|
53
|
-
- The `annotation_hash` (values for attributes) can be:
|
|
54
|
-
- a Hash mapping attribute name => value,
|
|
55
|
-
- a list of positional values that are zipped with the declared attribute names,
|
|
56
|
-
- a Hash with string keys (converted to symbols).
|
|
57
|
-
Examples:
|
|
58
|
-
```ruby
|
|
59
|
-
MyAnnotation.setup(obj, :code) # sets first attribute to :code (positional)
|
|
60
|
-
MyAnnotation.setup(obj, code: "some text") # sets code => "some text"
|
|
61
|
-
MyAnnotation.setup(obj, "code" => "v") # string keys are accepted
|
|
62
|
-
MyAnnotation.setup(obj, code2: :code) # remap behavior (see examples below)
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
- `Annotation.setup` convenience:
|
|
66
|
-
```ruby
|
|
67
|
-
# Apply one or more annotation modules
|
|
68
|
-
Annotation.setup(obj, [MyAnnotation, OtherAnnotation], code: "c", code3: "d")
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
Notes on calling `AnnotationModule.setup` (the module-level setup method used by both `Annotation.setup` and `AnnotationModule.setup`):
|
|
72
|
-
- If the target `obj` is frozen, it will be duplicated before extending.
|
|
73
|
-
- If a block is passed or `obj` is `nil` and a block is provided, the block (Proc) itself will be annotated (useful to attach annotations to callbacks).
|
|
74
|
-
- When multiple modules are applied to the same object, their annotations are merged; accessors are available for all annotated attributes.
|
|
75
|
-
|
|
76
|
-
Examples from tests:
|
|
77
|
-
```ruby
|
|
78
|
-
str = "String"
|
|
79
|
-
MyAnnotation.setup(str, :code)
|
|
80
|
-
# now str responds to `code` and `code` == :code
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
You can annotate other objects using an annotated object:
|
|
84
|
-
```ruby
|
|
85
|
-
a = MyAnnotation.setup("a", code: "c")
|
|
86
|
-
b = "b"
|
|
87
|
-
a.annotate(b) # copies the annotation values and types from a to b
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
---
|
|
91
|
-
|
|
92
|
-
## Annotated object API
|
|
93
|
-
|
|
94
|
-
When an object is annotated (extended with annotation modules), it gains these helpers (provided by `Annotation::AnnotatedObject`):
|
|
95
|
-
|
|
96
|
-
- `annotation_types` -> Array of annotation modules applied to the object.
|
|
97
|
-
- `annotation_hash` -> Hash mapping annotation attribute names (symbols) to values stored on the object.
|
|
98
|
-
- `annotation_info` -> combines `annotation_hash` plus:
|
|
99
|
-
- `annotation_types` (modules list),
|
|
100
|
-
- `annotated_array` boolean (true when object is an `AnnotatedArray`).
|
|
101
|
-
- `.serialize` (instance) and `AnnotatedObject.serialize(obj)` (class method) -> returns a purged `annotation_info` merged with a `literal: obj` entry (useful when creating stable representations).
|
|
102
|
-
- `annotation_id` / `id` -> deterministic digest built from the object and its annotation info (uses `Misc.digest` in the framework).
|
|
103
|
-
- `annotate(other)` -> applies all annotation types and attribute values of `self` onto `other`.
|
|
104
|
-
- `purge` -> returns a duplicate of the object with all annotation-related instance variables removed (`@annotations`, `@annotation_types`, `@container`).
|
|
105
|
-
- `make_array` -> returns a new array containing the object, annotated with the same annotation types/values, and extended as an `AnnotatedArray`.
|
|
106
|
-
|
|
107
|
-
Example:
|
|
108
|
-
```ruby
|
|
109
|
-
s = MyAnnotation.setup("s", code: "C")
|
|
110
|
-
s.annotation_hash # => { code: "C" }
|
|
111
|
-
s.id # => some digest
|
|
112
|
-
s2 = "other"
|
|
113
|
-
s.annotate(s2)
|
|
114
|
-
s2.code # => "C"
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
---
|
|
118
|
-
|
|
119
|
-
## Annotated arrays (AnnotatedArray)
|
|
120
|
-
|
|
121
|
-
`AnnotatedArray` is an array mixin that:
|
|
122
|
-
- stores annotation types/values at the array level,
|
|
123
|
-
- automatically annotates elements when they are accessed or iterated,
|
|
124
|
-
- provides container tracking on items: annotated items get `container` and `container_index` attributes (via `AnnotatedArrayItem`).
|
|
125
|
-
|
|
126
|
-
To make an array annotation-aware:
|
|
127
|
-
```ruby
|
|
128
|
-
AnnotationModule.setup(ary, code: "C")
|
|
129
|
-
ary.extend AnnotatedArray
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
Behavior and methods:
|
|
133
|
-
- Element access ([], first, last) returns annotated elements (unless `clean = true` passed to `[]`).
|
|
134
|
-
- `each`, `each_with_index`, `select`, `inject`, `collect` iterate over annotated items (so blocks receive annotated items).
|
|
135
|
-
- `compact`, `uniq`, `flatten`, `reverse`, `sort_by` are overridden to return annotated arrays (the result is annotated and extended with `AnnotatedArray`).
|
|
136
|
-
- `subset(list)` and `remove(list)` return new annotated arrays representing the set intersection/difference.
|
|
137
|
-
- Annotated array items receive `container` (the array) and `container_index` (the index position when produced via iteration/access).
|
|
138
|
-
|
|
139
|
-
Examples:
|
|
140
|
-
```ruby
|
|
141
|
-
ary = ["x"]
|
|
142
|
-
MyAnnotation.setup(ary, "C")
|
|
143
|
-
ary.extend AnnotatedArray
|
|
144
|
-
|
|
145
|
-
ary.code # => "C" (array-level)
|
|
146
|
-
ary[0].code # => "C" (element annotated)
|
|
147
|
-
ary.first.code # => "C"
|
|
148
|
-
ary.each { |e| puts e.code } # iterates annotated elements
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
`AnnotatedArrayItem` helpers:
|
|
152
|
-
- `container` -> reference to the array that annotated the item.
|
|
153
|
-
- `container_index` -> index position supplied by the array when returning that item.
|
|
154
|
-
|
|
155
|
-
Utility:
|
|
156
|
-
- `AnnotatedArray.is_contained?(obj)` -> true if obj is annotated as an `AnnotatedArrayItem`.
|
|
157
|
-
|
|
158
|
-
---
|
|
159
|
-
|
|
160
|
-
## Serialization and Marshal support
|
|
161
|
-
|
|
162
|
-
Annotations are stored as instance variables on the annotated objects; thus, `Marshal.dump` / `Marshal.load` preserve annotations and attribute values.
|
|
163
|
-
|
|
164
|
-
Example (from tests):
|
|
165
|
-
```ruby
|
|
166
|
-
a = MyAnnotation.setup("a", code: 'test1', code2: 'test2')
|
|
167
|
-
serialized = Marshal.dump(a)
|
|
168
|
-
a2 = Marshal.load(serialized)
|
|
169
|
-
a2.code # => 'test1'
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
Arrays extended with `AnnotatedArray` and annotated likewise survive Marshal roundtrip; loaded arrays still annotate their elements.
|
|
173
|
-
|
|
174
|
-
---
|
|
175
|
-
|
|
176
|
-
## Purging annotations
|
|
177
|
-
|
|
178
|
-
- AnnotatedObject#purge removes annotation instance variables from the object and returns a dup without annotations (`@annotations`, `@annotation_types`, `@container`).
|
|
179
|
-
- Annotation.purge(obj) is recursive:
|
|
180
|
-
- If obj is nil => returns nil.
|
|
181
|
-
- If obj is an annotated array => calls the object's purge and then purges each element recursively.
|
|
182
|
-
- If obj is an Array => returns an Array where each element is purged.
|
|
183
|
-
- If obj is a Hash => returns a new Hash with purged keys and values.
|
|
184
|
-
- Otherwise, if object is annotated (`Annotation.is_annotated?(obj)`), returns `obj.purge`, else returns the object itself.
|
|
185
|
-
|
|
186
|
-
Example:
|
|
187
|
-
```ruby
|
|
188
|
-
ary = ["string"]
|
|
189
|
-
MyAnnotation.setup(ary, "C")
|
|
190
|
-
ary.extend AnnotatedArray
|
|
191
|
-
|
|
192
|
-
Annotation.is_annotated?(ary) # => true
|
|
193
|
-
Annotation.is_annotated?(ary.first) # => true
|
|
194
|
-
|
|
195
|
-
purged = Annotation.purge(ary)
|
|
196
|
-
Annotation.is_annotated?(purged) # => false
|
|
197
|
-
Annotation.is_annotated?(purged.first) # => false
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
---
|
|
201
|
-
|
|
202
|
-
## Detection helpers
|
|
203
|
-
|
|
204
|
-
- `Annotation.is_annotated?(obj)` -> true if the object has been annotated (the object has an `@annotation_types` instance variable).
|
|
205
|
-
- `AnnotatedArray.is_contained?(obj)` -> true if object is an `AnnotatedArrayItem`.
|
|
206
|
-
|
|
207
|
-
---
|
|
208
|
-
|
|
209
|
-
## Extending and composing annotations
|
|
210
|
-
|
|
211
|
-
- Multiple annotation modules can be applied to the same object. Their attributes and values are merged on the object.
|
|
212
|
-
- Annotation modules may `include` other annotation modules. When a module including another annotation module is itself extended into an object, the included module's declared attributes are propagated.
|
|
213
|
-
- When a module `extend Annotation`, the `Annotation.extended` hook ensures:
|
|
214
|
-
- `@annotations` is initialized,
|
|
215
|
-
- the module includes `Annotation::AnnotatedObject` (so annotated objects get object helpers),
|
|
216
|
-
- the module extends `Annotation::AnnotationModule` (which implements `annotation`, `setup`, and include/extend integration code).
|
|
217
|
-
|
|
218
|
-
Example of composing annotations:
|
|
219
|
-
```ruby
|
|
220
|
-
module A
|
|
221
|
-
extend Annotation
|
|
222
|
-
annotation :a1
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
module B
|
|
226
|
-
extend Annotation
|
|
227
|
-
annotation :b1
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
obj = "s"
|
|
231
|
-
Annotation.setup(obj, [A, B], a1: 'one', b1: 'two')
|
|
232
|
-
# obj now responds to a1, b1
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
---
|
|
236
|
-
|
|
237
|
-
## Notes and edge cases
|
|
238
|
-
|
|
239
|
-
- `Annotation.setup(obj, ...)` returns `nil` immediately if `obj.nil?`.
|
|
240
|
-
- If the target object is frozen, the setup will duplicate it before extending.
|
|
241
|
-
- `AnnotationModule.setup` can accept positional values (zipped with the declared attributes) or a hash mapping attribute names to values.
|
|
242
|
-
- Example: `MyModule.setup(obj, :val_for_first)` sets the first declared attribute to `:val_for_first`.
|
|
243
|
-
- Example: `MyModule.setup(obj, :a => 1, :b => 2)` sets attributes by name.
|
|
244
|
-
- You can annotate a block/proc by passing a block to `setup` (or passing `nil` as the object and supplying a block). The block (Proc) will be extended with the annotation module.
|
|
245
|
-
- `Annotation.setup` accepts a third argument (`annotation_hash`) or positional values similar to the module-level `setup`. It also accepts `annotation_types` as a string with `|` separated module names, an Array of modules, or a single module.
|
|
246
|
-
|
|
247
|
-
---
|
|
248
|
-
|
|
249
|
-
## API quick reference
|
|
250
|
-
|
|
251
|
-
Annotation module-level:
|
|
252
|
-
- Annotation.setup(obj, annotation_types, annotation_hash_or_positional_values)
|
|
253
|
-
- obj: object to annotate (String, Array, Array instance, Proc, etc.)
|
|
254
|
-
- annotation_types: Module, String (module names separated by `|`), or Array of modules
|
|
255
|
-
- annotation_hash_or_positional_values: Hash or positional values mapped to declared attributes
|
|
256
|
-
- returns the annotated object (or nil if obj.nil?)
|
|
257
|
-
|
|
258
|
-
- Annotation.extended(base) (internal hook) — prepares modules that `extend Annotation`.
|
|
259
|
-
- Annotation.is_annotated?(obj) -> boolean
|
|
260
|
-
- Annotation.purge(obj) -> returns object or a purged (annotation-free) copy/structure
|
|
261
|
-
|
|
262
|
-
Annotation::AnnotationModule (module methods available on modules that `extend Annotation`):
|
|
263
|
-
- annotation(*attrs) -> declare attributes and create accessors
|
|
264
|
-
- annotations -> declared attributes list
|
|
265
|
-
- included(mod) — when the annotation module is included in another module, merges declared attributes
|
|
266
|
-
- extended(obj) — when the annotation module is extended into an object, sets up `@annotations` and registers this module into the object's `annotation_types`
|
|
267
|
-
- setup(obj, *values_or_hash, &block) -> annotate `obj` (or block) with this module and set attribute values
|
|
268
|
-
|
|
269
|
-
Annotation::AnnotatedObject (instance methods added to annotated objects):
|
|
270
|
-
- annotation_types -> array of modules applied
|
|
271
|
-
- annotation_hash -> Hash of attribute names => values
|
|
272
|
-
- annotation_info -> combines annotation_hash + metadata
|
|
273
|
-
- serialize / AnnotatedObject.serialize(obj) -> purged annotation_info merged with literal
|
|
274
|
-
- annotation_id / id -> digest based id
|
|
275
|
-
- annotate(other) -> copy annotations onto `other`
|
|
276
|
-
- purge -> duplicate object and remove annotation instance variables
|
|
277
|
-
- make_array -> wrap object into annotated array
|
|
278
|
-
|
|
279
|
-
AnnotatedArray (array-level helpers):
|
|
280
|
-
- extend AnnotatedArray to annotate arrays and propagate annotations to their items
|
|
281
|
-
- annotate_item(obj, position = nil) -> annotate an item and set container/container_index
|
|
282
|
-
- [] (overridden), first, last, each, each_with_index, select, inject, collect, compact, uniq, flatten, reverse, sort_by, subset, remove
|
|
283
|
-
|
|
284
|
-
---
|
|
285
|
-
|
|
286
|
-
## Examples (from tests)
|
|
287
|
-
|
|
288
|
-
Define annotation modules:
|
|
289
|
-
|
|
290
|
-
```ruby
|
|
291
|
-
module AnnotationClass
|
|
292
|
-
extend Annotation
|
|
293
|
-
annotation :code, :code2
|
|
294
|
-
end
|
|
295
|
-
|
|
296
|
-
module AnnotationClass2
|
|
297
|
-
extend Annotation
|
|
298
|
-
annotation :code3, :code4
|
|
299
|
-
end
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
Annotate a string:
|
|
303
|
-
|
|
304
|
-
```ruby
|
|
305
|
-
str = "String"
|
|
306
|
-
AnnotationClass.setup(str, :code) # sets str.code == :code
|
|
307
|
-
AnnotationClass2.setup(str, :c3, :c4)
|
|
308
|
-
# str now includes both annotation modules and has code/code2/code3/code4
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
Annotate arrays and propagate to elements:
|
|
312
|
-
|
|
313
|
-
```ruby
|
|
314
|
-
ary = ["string"]
|
|
315
|
-
AnnotationClass.setup(ary, "Annotation String")
|
|
316
|
-
ary.extend AnnotatedArray
|
|
317
|
-
ary.code # => "Annotation String"
|
|
318
|
-
ary[0].code # => "Annotation String"
|
|
319
|
-
ary.first.code # => "Annotation String"
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
Purge annotations:
|
|
323
|
-
|
|
324
|
-
```ruby
|
|
325
|
-
ary = ["string"]
|
|
326
|
-
AnnotationClass.setup(ary, "C")
|
|
327
|
-
ary.extend AnnotatedArray
|
|
328
|
-
|
|
329
|
-
purged = Annotation.purge(ary)
|
|
330
|
-
# purged and purged.first are not annotated anymore
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
Marshal roundtrip preserves annotations:
|
|
334
|
-
|
|
335
|
-
```ruby
|
|
336
|
-
a = AnnotationClass.setup("a", code: 'test1', code2: 'test2')
|
|
337
|
-
d = Marshal.dump(a)
|
|
338
|
-
a2 = Marshal.load(d)
|
|
339
|
-
a2.code # => 'test1'
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
Annotating a block:
|
|
343
|
-
|
|
344
|
-
```ruby
|
|
345
|
-
# annotate the block (proc) itself
|
|
346
|
-
proc_obj = AnnotationClass.setup(nil, code: :c) do
|
|
347
|
-
puts "hello"
|
|
348
|
-
end
|
|
349
|
-
proc_obj.code # => :c
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
This document covers the primary use and behaviors of Annotation, the annotation modules that extend it, the AnnotatedObject helpers added to annotated objects, and the AnnotatedArray behaviors. Use the examples above as templates to create, combine, and apply annotations to objects and collections.
|