xqsr3 0.39.10 → 0.39.11

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +22 -0
  3. data/EXAMPLES.md +4 -5
  4. data/FAQ.md +0 -3
  5. data/NEWS.md +1 -0
  6. data/README.md +24 -20
  7. data/TODO.md +17 -7
  8. data/docs/components/README.md +61 -0
  9. data/docs/components/array-utilities.md +120 -0
  10. data/docs/components/command-line-utilities.md +151 -0
  11. data/docs/components/containers.md +143 -0
  12. data/docs/components/conversion.md +166 -0
  13. data/docs/components/diagnostics.md +210 -0
  14. data/docs/components/extensions.md +232 -0
  15. data/docs/components/hash-utilities.md +192 -0
  16. data/docs/components/io.md +188 -0
  17. data/docs/components/quality.md +251 -0
  18. data/docs/components/string-utilities.md +218 -0
  19. data/docs/guides/README.md +23 -0
  20. data/docs/guides/choosing-a-component.md +183 -0
  21. data/docs/guides/formatting-and-writing-output.md +182 -0
  22. data/docs/guides/getting-started.md +181 -0
  23. data/docs/guides/handling-failures.md +157 -0
  24. data/docs/guides/parsing-and-validating-input.md +262 -0
  25. data/docs/guides/processing-collections.md +195 -0
  26. data/docs/reference/README.md +38 -0
  27. data/examples/count_word_frequencies.md +3 -2
  28. data/lib/xqsr3/array_utilities/join_with_or.rb +3 -2
  29. data/lib/xqsr3/command_line_utilities/map_option_string.rb +3 -2
  30. data/lib/xqsr3/containers/frequency_map.rb +53 -53
  31. data/lib/xqsr3/containers/multi_map.rb +5 -4
  32. data/lib/xqsr3/conversion/bool_parser.rb +5 -4
  33. data/lib/xqsr3/conversion/integer_parser.rb +4 -4
  34. data/lib/xqsr3/diagnostics/exception_utilities.rb +7 -4
  35. data/lib/xqsr3/diagnostics/exceptions/with_cause.rb +24 -16
  36. data/lib/xqsr3/diagnostics/inspect_builder.rb +2 -2
  37. data/lib/xqsr3/doc_.rb +20 -5
  38. data/lib/xqsr3/extensions/hash/slice.rb +2 -0
  39. data/lib/xqsr3/extensions/integer/to_s_grp.rb +3 -2
  40. data/lib/xqsr3/extensions/string/map_option_string.rb +2 -0
  41. data/lib/xqsr3/hash_utilities/key_matching.rb +3 -3
  42. data/lib/xqsr3/quality/parameter_checking.rb +17 -16
  43. data/lib/xqsr3/string_utilities/ends_with.rb +3 -3
  44. data/lib/xqsr3/string_utilities/nil_if_empty.rb +3 -3
  45. data/lib/xqsr3/string_utilities/nil_if_whitespace.rb +3 -3
  46. data/lib/xqsr3/string_utilities/quote_if.rb +3 -2
  47. data/lib/xqsr3/string_utilities/starts_with.rb +3 -3
  48. data/lib/xqsr3/version.rb +2 -2
  49. metadata +21 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ce217f9cec0cb4a4a2b70759636345add271ecd29e894b755af95eb02c189df
4
- data.tar.gz: 85997e37e26ac33b6d44e19d6923e476a5f0e3826381f6344c1f4f438dbb00b4
3
+ metadata.gz: 7be376b875d05077dcd53dc79b7a85d79f23931761ba1c765b9015f4b29a06d2
4
+ data.tar.gz: 7e78f40749a6a290600acf1d2695b8e45634be651dfa752fb23423d227283e6f
5
5
  SHA512:
6
- metadata.gz: 7164b91b2b8d997ab35fbe261de3be3c445745c3d46a4c378583fcced4d5da7873e1bc0614b6ef2a4a5cba6c1b0f6f079b7374d9ca4794fa53bc6c353779ae64
7
- data.tar.gz: a45ff408ba9fe2bd52b3733430b8390c01a9c032918a8070190abc443bdae2d20fdb13c8f1b92412e8da077b2a35458592e17930e48be9af0d88761a00a109f4
6
+ metadata.gz: 02ca2e98c607a51edbccb38082527fc2c9d800e6c5f632d8826296f16e114682c02b83bdf6ba5be747eaf444694a90efdeaa7cf96580d39fa1e7097148fb167a
7
+ data.tar.gz: f8500bc1f235ea1ef60de441641edb9b371563cb58102d923d0b482725a427cd0c115bf6366a5718e070e81fe0d52c3da827f98951c5f138413d932cc00987b8
data/CHANGES.md CHANGED
@@ -1,6 +1,28 @@
1
1
  # xqsr3 - Changes <!-- omit in toc -->
2
2
 
3
3
 
4
+ ## 0.39.11 - 29th August 2026
5
+
6
+ * Added a user-oriented component catalogue under **docs/components/**, covering standalone components, extensions, loading paths, API summaries, and representative usage;
7
+ * Added task-oriented guides under **docs/guides/** for getting started, component selection, input parsing and validation, collection processing, failure handling, and output formatting;
8
+ * Normalised legacy Markdown inline-code, checklist, table, and heading markup in **README.md**, **EXAMPLES.md**, **FAQ.md**, **TODO.md**, and the example guide;
9
+ * Added **docs/reference/README.md** describing the generated RDoc reference, and updated the Unix and Windows RDoc helpers to exclude authored **docs/** and **examples/** content from generated output;
10
+ * Included authored **docs/** content in the gem package while continuing to exclude generated **doc/** output;
11
+ * Set the generated RDoc title to the **xqsr3** API reference, and excluded project boilerplate pages from the reference;
12
+ * Expanded source-level RDoc documentation and visibility annotations for the public API, including **FrequencyMap**, **BoolParser**, **ExceptionUtilities**, **WithCause**, **ParameterChecking**, and extension classes;
13
+ * Replaced a remaining placeholder example and legacy RDoc markup in public API comments;
14
+ * Extended **README.md** with linked component categories and navigation to the component catalogue and user guides;
15
+ * Added a GitHub Actions Documentation job to **.github/workflows/ruby.yml** that generates an RDoc coverage report under Ruby 3.4, and aligned its push branch triggers with the canonical set;
16
+ * Added Ruby 4.0 to the GitHub Actions test matrix across the supported operating systems;
17
+ * Made the RDoc coverage check fail when a documentable API entity is undocumented;
18
+ * Added `--help` support to the Unix and Windows RDoc helpers, using project metadata from **.sis/**;
19
+ * Made the Unix and Windows RDoc helpers runnable from any working directory, with `--pwd` selecting the caller's directory and `SIS_RDOC_DOC_DIR` controlling the generated-document directory;
20
+ * Added documentation follow-up items to **TODO.md**;
21
+ * Removed the **.vscode/** ignore rule from **.gitignore**;
22
+ * Refreshed `Updated:` fields and copyright date ranges in modified library sources;
23
+ * Bumped the library version to 0.39.11 and recorded the release in **NEWS.md**;
24
+
25
+
4
26
  ## 0.39.10 - 28th August 2026
5
27
 
6
28
  * **Gemfile.lock** is no longer tracked in the repository, completing the **0.39.9** lockfile work (`lockfile false` in **Gemfile**, **.gitignore** entry, and `spec.files` exclusion in **xqsr3.gemspec**);
data/EXAMPLES.md CHANGED
@@ -1,9 +1,8 @@
1
1
  # xqsr3 - Examples <!-- omit in toc -->
2
2
 
3
- |Name|Source & Description|Summary|
4
- |---|---|---|
5
- |**count_word_frequencies**|[examples/count_word_frequencies.rb](./examples/count_word_frequencies.rb)<br/>[examples/count_word_frequencies.md](./examples/count_word_frequencies.md)|Simple example supporting ```--help``` and ```--version```|
3
+ | Name | Source & Description | Summary |
4
+ | -------------------------- | -------------------- | ------- |
5
+ | **count_word_frequencies** | [examples/count_word_frequencies.rb](./examples/count_word_frequencies.rb)<br/>[examples/count_word_frequencies.md](./examples/count_word_frequencies.md) | Simple example supporting `--help` and `--version` |
6
6
 
7
7
 
8
- <!-- ########################### end of file ########################### -->
9
-
8
+ <!-- ########################### end of file ########################### -->
data/FAQ.md CHANGED
@@ -10,9 +10,6 @@ it will be used to create one.
10
10
  - [Q1: "How do I install this library?"](#q1-how-do-i-install-this-library)
11
11
 
12
12
 
13
- # FAQs: <!-- omit in toc -->
14
-
15
-
16
13
  ## Q1: "How do I install this library?"
17
14
 
18
15
  Install via **gem**:
data/NEWS.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  | Date | News Item |
4
4
  | ------------------- | ------------------------------------------------------------------------------------ |
5
+ | 29th August 2026 | [**xqsr3** 0.39.11](https://github.com/synesissoftware/xqsr3/releases/tag/0.39.11) |
5
6
  | 28th August 2026 | [**xqsr3** 0.39.10](https://github.com/synesissoftware/xqsr3/releases/tag/0.39.10) |
6
7
  | 21st August 2026 | [**xqsr3** 0.39.9](https://github.com/synesissoftware/xqsr3/releases/tag/0.39.9) |
7
8
  | 20th August 2026 | [**xqsr3** 0.39.8](https://github.com/synesissoftware/xqsr3/releases/tag/0.39.8) |
data/README.md CHANGED
@@ -49,13 +49,13 @@ gem install xqsr3
49
49
  or add it to your `Gemfile`.
50
50
 
51
51
  Use is via specific APIs or groups. For example, in order to use the
52
- ``FrequencyMap`` class you would ``require`` the source file, as in:
52
+ `FrequencyMap` class you would `require` the source file, as in:
53
53
 
54
54
  ```Ruby
55
55
  require 'xqsr3/containers/frequency_map'
56
56
  ```
57
57
 
58
- Alternatively, to use all **test/unit** extensions you would ``require`` all
58
+ Alternatively, to use _all_ **test/unit** extensions you would `require` all
59
59
  relative via the file:
60
60
 
61
61
  ```Ruby
@@ -69,34 +69,38 @@ which brings in nine extensions.
69
69
 
70
70
  **xqsr3** provides components in the following categories:
71
71
 
72
- * Array Utilities
73
- * Command-line Utilities
74
- * Containers
75
- * Conversion
76
- * Diagnostics
77
- * Hash Utilities
78
- * IO
79
- * Quality
80
- * String Utilities
72
+ * [Array Utilities](./docs/components/array-utilities.md);
73
+ * [Command-line Utilities](./docs/components/command-line-utilities.md);
74
+ * [Containers](./docs/components/containers.md);
75
+ * [Conversion](./docs/components/conversion.md);
76
+ * [Diagnostics](./docs/components/diagnostics.md);
77
+ * [Hash Utilities](./docs/components/hash-utilities.md);
78
+ * [IO](./docs/components/io.md);
79
+ * [Quality](./docs/components/quality.md);
80
+ * [String Utilities](./docs/components/string-utilities.md);
81
81
  * ~~XML Utilities~~ **NOTE**: The **XML** components formerly in **xqsr3** in
82
82
  versions **0.29**-**0.30** are now contained in the separate project
83
83
  [**xqsr3-xml**](https://github.com/synesissoftware/xqsr3-xml/).
84
84
 
85
85
  and extensions to the following standard library components:
86
86
 
87
- * Array extensions
88
- * Enumerable extensions
89
- * Hash extensions
90
- * Integer extensions
91
- * IO extensions
92
- * Kernel extensions
93
- * String extensions
94
- * test/unit extensions
87
+ * [Array extensions](./docs/components/extensions.md#array-extensions);
88
+ * [Enumerable extensions](./docs/components/extensions.md#enumerable-extensions);
89
+ * [Hash extensions](./docs/components/extensions.md#hash-extensions);
90
+ * [Integer extensions](./docs/components/extensions.md#integer-extensions);
91
+ * [IO extensions](./docs/components/extensions.md#io-extensions);
92
+ * [Kernel extensions](./docs/components/extensions.md#kernel-extensions);
93
+ * [String extensions](./docs/components/extensions.md#string-extensions);
94
+ * [test/unit extensions](./docs/components/extensions.md#testunit-extensions);
95
+
96
+ The complete [component catalogue](./docs/components/README.md) provides
97
+ loading instructions and initial API summaries. The task-oriented guides are
98
+ available under [docs/guides/](./docs/guides/README.md).
95
99
 
96
100
 
97
101
  ## Examples
98
102
 
99
- Examples are provided in the ```examples``` directory, along with a markdown description for each. A detailed list TOC of them is provided in [EXAMPLES.md](./EXAMPLES.md).
103
+ Examples are provided in the `examples` directory, along with a markdown description for each. A detailed list TOC of them is provided in [EXAMPLES.md](./EXAMPLES.md).
100
104
 
101
105
 
102
106
  ## Project Information
data/TODO.md CHANGED
@@ -3,8 +3,18 @@
3
3
 
4
4
  ## Functional improvements
5
5
 
6
- * [x] ~~~prepare `IO.writelines` (and related helpers) for frozen-string-literal defaults (Ruby 3.4+ warnings under `-W`)~~~;
7
- * [x] ~~~quiet Ruby 3.4 `test-unit` warnings for blocks passed to `assert_nil` / `assert_not_nil` in **test/unit/quality/tc_parameter_checking.rb**~~~;
6
+ * [x] ~~~prepare `IO.writelines` (and related helpers) for frozen-string-literal defaults (Ruby 3.4+ warnings under `-W`)~~~ - ✅;
7
+ * [x] ~~~quiet Ruby 3.4 `test-unit` warnings for blocks passed to `assert_nil` / `assert_not_nil` in **test/unit/quality/tc_parameter_checking.rb**~~~ - ✅;
8
+
9
+
10
+ ## Documentation
11
+
12
+ * [x] ~~~curated component catalogue under **docs/components/**~~~ - ✅;
13
+ * [x] ~~~task-oriented user guide under **docs/guides/**~~~ - ✅;
14
+ * [ ] Additional example programs covering the public component categories;
15
+ * [x] ~~~Expanded generated API reference~~~ - ✅;
16
+ * [ ] Executable cookbook and recipe documentation;
17
+ * [ ] Static documentation website with search and versioned references;
8
18
 
9
19
 
10
20
  ## Performance improvements
@@ -14,11 +24,11 @@
14
24
 
15
25
  ## Packaging improvements
16
26
 
17
- * [ ] **README.md** and **docs/*.md** introductory elements of main features;
18
- * [x] ~~~remove **Gemfile.lock**~~~;
19
- * [x] ~~~obtain a **run_all_unit_tests.sh** (from **misc-dev-scripts**) that skips `tput` when `$TERM` is unset or stdout is not a TTY (CI: `tput: No value for $TERM and no -T specified`)~~~;
20
- * [x] ~~~after the packaging/boilerplate/CI baseline release: bump **VERSION**, drop gemspec `required_ruby_version` `< 4` upper bound, and align **CHANGES**~~~;
21
- * [x] ~~~gemspec polish: `https` homepage, Rubygems `metadata` URIs, stop using `Date.today`, include **CHANGES.md** in packaged files~~~;
27
+ * [x] ~~~**README.md** and **docs/*.md** introductory elements of main features~~~ - ✅;
28
+ * [x] ~~~remove **Gemfile.lock**~~~ - ✅;
29
+ * [x] ~~~obtain a **run_all_unit_tests.sh** (from **misc-dev-scripts**) that skips `tput` when `$TERM` is unset or stdout is not a TTY (CI: `tput: No value for $TERM and no -T specified`)~~~ - ✅;
30
+ * [x] ~~~after the packaging/boilerplate/CI baseline release: bump **VERSION**, drop gemspec `required_ruby_version` `< 4` upper bound, and align **CHANGES**~~~ - ✅;
31
+ * [x] ~~~gemspec polish: `https` homepage, Rubygems `metadata` URIs, stop using `Date.today`, include **CHANGES.md** in packaged files~~~ - ✅;
22
32
 
23
33
 
24
34
  <!-- ########################### end of file ########################### -->
@@ -0,0 +1,61 @@
1
+ # xqsr3 Component Catalogue <!-- omit in toc -->
2
+
3
+ This catalogue is the user-oriented guide to the components provided by
4
+ **xqsr3**. It is organised by component category rather than by Ruby source
5
+ file.
6
+
7
+
8
+ ## Table of Contents <!-- omit in toc -->
9
+
10
+ - [Using the catalogue](#using-the-catalogue)
11
+ - [Categories](#categories)
12
+ - [Related workflows](#related-workflows)
13
+
14
+
15
+ ## Using the catalogue
16
+
17
+ Components are loaded explicitly. Each category page documents the relevant
18
+ `require` path, public entry points, and representative usage.
19
+
20
+ The catalogue documents the supported public surface. Internal files and
21
+ implementation details are intentionally excluded.
22
+
23
+ The ten category pages correspond to the public top-level component entry
24
+ points under `lib/xqsr3/`. The `Extensions` page additionally covers the
25
+ opt-in monkey patches, while `all_extensions.rb` is documented as its
26
+ broad-scope loading choice.
27
+
28
+
29
+ ## Categories
30
+
31
+ * [Array Utilities](./array-utilities.md);
32
+ * [Command-line Utilities](./command-line-utilities.md);
33
+ * [Containers](./containers.md);
34
+ * [Conversion](./conversion.md);
35
+ * [Diagnostics](./diagnostics.md);
36
+ * [Extensions](./extensions.md);
37
+ * [Hash Utilities](./hash-utilities.md);
38
+ * [IO](./io.md);
39
+ * [Quality](./quality.md);
40
+ * [String Utilities](./string-utilities.md);
41
+
42
+
43
+ ## Related workflows
44
+
45
+ Use the catalogue alongside the task-oriented guides:
46
+
47
+ * [Choosing a Component](../guides/choosing-a-component.md) explains how to
48
+ select a category and loading scope;
49
+ * [Getting Started](../guides/getting-started.md) demonstrates a minimal
50
+ application workflow;
51
+ * [Parsing and Validating External Input](../guides/parsing-and-validating-input.md)
52
+ combines string, conversion, and quality components;
53
+ * [Processing Collections](../guides/processing-collections.md) combines
54
+ containers, hash utilities, and extensions;
55
+ * [Handling Failures](../guides/handling-failures.md) combines quality and
56
+ diagnostics components;
57
+ * [Formatting and Writing Output](../guides/formatting-and-writing-output.md)
58
+ combines array, IO, and string utilities.
59
+
60
+
61
+ <!-- ########################### end of file ########################### -->
@@ -0,0 +1,120 @@
1
+ # xqsr3 Array Utilities <!-- omit in toc -->
2
+
3
+ Array utilities provide standalone operations for working with Ruby arrays.
4
+ The current component is a formatter for presenting alternatives in
5
+ human-readable text.
6
+
7
+
8
+ ## Table of Contents <!-- omit in toc -->
9
+
10
+ - [Loading](#loading)
11
+ - [When to use it](#when-to-use-it)
12
+ - [JoinWithOr](#joinwithor)
13
+ - [Formatting options](#formatting-options)
14
+ - [Standalone and extension forms](#standalone-and-extension-forms)
15
+
16
+
17
+ ## Loading
18
+
19
+ ```Ruby
20
+ require 'xqsr3/array_utilities'
21
+ ```
22
+
23
+ Or load the component directly:
24
+
25
+ ```Ruby
26
+ require 'xqsr3/array_utilities/join_with_or'
27
+ ```
28
+
29
+ The standalone module is
30
+ `Xqsr3::ArrayUtilities::JoinWithOr`.
31
+
32
+
33
+ ## When to use it
34
+
35
+ Use `join_with_or` for messages that describe alternatives, such as accepted
36
+ formats, permitted values, or choices presented to a user. It is not a
37
+ general-purpose serialization method: it adds grammatical spacing and an
38
+ optional Oxford comma.
39
+
40
+
41
+ ## `JoinWithOr`
42
+
43
+ `Xqsr3::ArrayUtilities::JoinWithOr.join_with_or` formats values using these
44
+ cardinality rules:
45
+
46
+ * `nil` and an empty array produce `''`;
47
+ * one value produces that value;
48
+ * two values are joined with `or` and no comma;
49
+ * three or more values use commas and, by default, an Oxford comma before
50
+ `or`.
51
+
52
+ ```Ruby
53
+ require 'xqsr3/array_utilities/join_with_or'
54
+
55
+ formatter = Xqsr3::ArrayUtilities::JoinWithOr
56
+
57
+ formatter.join_with_or([])
58
+ # => ''
59
+ formatter.join_with_or(['red'])
60
+ # => 'red'
61
+ formatter.join_with_or(['red', 'green'])
62
+ # => 'red or green'
63
+ formatter.join_with_or(['red', 'green', 'blue'])
64
+ # => 'red, green, or blue'
65
+ ```
66
+
67
+ Values are interpolated into the result, so they do not need to be strings.
68
+ The array argument itself must be an `Array` or `nil`; another type raises
69
+ `TypeError`.
70
+
71
+
72
+ ## Formatting options
73
+
74
+ The `or` word, separator, Oxford-comma policy, and quote character are
75
+ independent options:
76
+
77
+ ```Ruby
78
+ formatter.join_with_or(
79
+ ['red', 'green', 'blue'],
80
+ or: 'OR',
81
+ separator: ';',
82
+ oxford_comma: false,
83
+ quote_char: '"',
84
+ )
85
+ # => '"red"; "green" OR "blue"'
86
+ ```
87
+
88
+ The options are:
89
+
90
+ * `or` replaces the default word `or`;
91
+ * `separator` replaces the default comma between items;
92
+ * `oxford_comma: false` removes the separator before `or` for lists of three
93
+ or more values;
94
+ * `quote_char` surrounds every value with the supplied character.
95
+
96
+ The Oxford-comma option has no effect for zero, one, or two values. A
97
+ `quote_char` is applied literally to both sides and does not escape quote
98
+ characters already present in a value.
99
+
100
+
101
+ ## Standalone and extension forms
102
+
103
+ The extension form adds `Array#join_with_or`:
104
+
105
+ ```Ruby
106
+ require 'xqsr3/extensions/array/join_with_or'
107
+
108
+ ['red', 'green', 'blue'].join_with_or
109
+ # => 'red, green, or blue'
110
+ ```
111
+
112
+ The extension delegates to the standalone implementation. Use the standalone
113
+ form in reusable libraries that want to avoid modifying `Array`; use the
114
+ extension form when method syntax improves the surrounding application code.
115
+
116
+ For executable behavioural examples, see
117
+ `test/unit/array_utilities/tc_join_with_or.rb`.
118
+
119
+
120
+ <!-- ########################### end of file ########################### -->
@@ -0,0 +1,151 @@
1
+ # xqsr3 Command-line Utilities <!-- omit in toc -->
2
+
3
+ Command-line utilities support compact mapping from user-facing option text
4
+ to stable Ruby symbols. The component is particularly useful when a command
5
+ line accepts both long option names and declared single-letter shortcuts.
6
+
7
+
8
+ ## Table of Contents <!-- omit in toc -->
9
+
10
+ - [Loading](#loading)
11
+ - [When to use it](#when-to-use-it)
12
+ - [MapOptionString](#mapoptionstring)
13
+ - [Option-string grammar](#option-string-grammar)
14
+ - [Standalone and extension forms](#standalone-and-extension-forms)
15
+ - [Failure and matching behaviour](#failure-and-matching-behaviour)
16
+
17
+
18
+ ## Loading
19
+
20
+ Load the command-line utility category:
21
+
22
+ ```Ruby
23
+ require 'xqsr3/command_line_utilities'
24
+ ```
25
+
26
+ Or load the component directly:
27
+
28
+ ```Ruby
29
+ require 'xqsr3/command_line_utilities/map_option_string'
30
+ ```
31
+
32
+ The module is defined as
33
+ `Xqsr3::CommandLineUtilities::MapOptionString`.
34
+
35
+
36
+ ## When to use it
37
+
38
+ Use `MapOptionString` when the application has a fixed set of option
39
+ spellings and wants the result to be a symbol suitable for dispatch:
40
+
41
+ ```Ruby
42
+ options = ['help', 'version', 'verbose']
43
+ option = 'verbose'
44
+
45
+ Xqsr3::CommandLineUtilities::MapOptionString
46
+ .map_option_string_from_string(option, options)
47
+ # => :verbose
48
+ ```
49
+
50
+ It is not a complete command-line parser. It does not consume `ARGV`, parse
51
+ option arguments, or validate an option's value.
52
+
53
+
54
+ ## `MapOptionString`
55
+
56
+ `map_option_string_from_string(string, option_strings)` returns the symbol
57
+ corresponding to the first matching declared option. A declared option
58
+ without shortcut syntax maps to itself:
59
+
60
+ ```Ruby
61
+ require 'xqsr3/command_line_utilities/map_option_string'
62
+
63
+ mapper = Xqsr3::CommandLineUtilities::MapOptionString
64
+ declared = ['help', 'version', 'dry-run']
65
+
66
+ mapper.map_option_string_from_string('help', declared)
67
+ # => :help
68
+ mapper.map_option_string_from_string('dry-run', declared)
69
+ # => :dry_run
70
+ mapper.map_option_string_from_string('unknown', declared)
71
+ # => nil
72
+ ```
73
+
74
+ The canonical option text is converted to a symbol using the String Utilities
75
+ symbol rules. Consequently, hyphens become underscores in the returned
76
+ symbol.
77
+
78
+
79
+ ## Option-string grammar
80
+
81
+ Place each shortcut character in square brackets within its long spelling:
82
+
83
+ ```Ruby
84
+ declared = ['[h]elp', '[v]ersion', '[d]ry-[r]un']
85
+
86
+ mapper.map_option_string_from_string('h', declared)
87
+ # => :help
88
+ mapper.map_option_string_from_string('help', declared)
89
+ # => :help
90
+ mapper.map_option_string_from_string('dr', declared)
91
+ # => :dry_run
92
+ mapper.map_option_string_from_string('dry-run', declared)
93
+ # => :dry_run
94
+ ```
95
+
96
+ Every bracketed character contributes to the shortcut. Thus `[d]ry-[r]un`
97
+ declares `dr`; the unbracketed long form remains `dry-run`. Text between
98
+ brackets is retained in the long spelling and removed from the shortcut.
99
+
100
+ Shortcut matching is exact. Partial long names and individual unbracketed
101
+ characters do not match:
102
+
103
+ ```Ruby
104
+ mapper.map_option_string_from_string('d', declared)
105
+ # => nil
106
+ mapper.map_option_string_from_string('dry', declared)
107
+ # => nil
108
+ ```
109
+
110
+
111
+ ## Standalone and extension forms
112
+
113
+ The standalone form is useful when the caller wants explicit dependencies:
114
+
115
+ ```Ruby
116
+ Xqsr3::CommandLineUtilities::MapOptionString
117
+ .map_option_string_from_string('v', ['[v]ersion'])
118
+ # => :version
119
+ ```
120
+
121
+ The extension form adds `String#map_option_string`:
122
+
123
+ ```Ruby
124
+ require 'xqsr3/extensions/string/map_option_string'
125
+
126
+ 'v'.map_option_string(['[v]ersion'])
127
+ # => :version
128
+ ```
129
+
130
+ The extension also defines `NilClass#map_option_string`, which returns `nil`.
131
+ Other receiver types must respond to `to_str`.
132
+
133
+
134
+ ## Failure and matching behaviour
135
+
136
+ An unmatched input returns `nil`; no exception is raised for an ordinary
137
+ non-match. Matching is performed in declaration order, so duplicate or
138
+ overlapping declarations should be avoided.
139
+
140
+ The canonical option string is converted to a symbol only after a match.
141
+ Names containing characters that the symbol converter rejects can therefore
142
+ produce `nil` even though the option text matched.
143
+
144
+ The optional `options` argument is accepted for interface compatibility but
145
+ does not currently alter the mapping rules.
146
+
147
+ For executable behavioural examples, see
148
+ `test/unit/command_line_utilities/tc_map_option_string.rb`.
149
+
150
+
151
+ <!-- ########################### end of file ########################### -->
@@ -0,0 +1,143 @@
1
+ # xqsr3 Containers <!-- omit in toc -->
2
+
3
+ The container components provide focused collection types for common
4
+ counting and key-to-multiple-value use cases.
5
+
6
+ Use `FrequencyMap` when each key has one numeric count. Use `MultiMap` when
7
+ each key has an ordered collection of values. Both preserve the distinction
8
+ between the number of keys and the number of associated items.
9
+
10
+
11
+ ## Table of Contents <!-- omit in toc -->
12
+
13
+ - [Loading](#loading)
14
+ - [Choosing a container](#choosing-a-container)
15
+ - [FrequencyMap](#frequencymap)
16
+ - [MultiMap](#multimap)
17
+ - [Shared conventions](#shared-conventions)
18
+
19
+
20
+ ## Loading
21
+
22
+ Load both containers through the category entry point:
23
+
24
+ ```Ruby
25
+ require 'xqsr3/containers'
26
+ ```
27
+
28
+ Or load only the component required by the application:
29
+
30
+ ```Ruby
31
+ require 'xqsr3/containers/frequency_map'
32
+ require 'xqsr3/containers/multi_map'
33
+ ```
34
+
35
+ The classes are defined in `Xqsr3::Containers`.
36
+
37
+
38
+ ## Choosing a container
39
+
40
+ * Choose `FrequencyMap` for histograms, frequency tables, and tallying;
41
+ * Choose `MultiMap` for one-to-many relationships and grouped values;
42
+ * Choose a regular `Hash` when neither counting nor one-to-many behaviour is
43
+ required.
44
+
45
+
46
+ ## `FrequencyMap`
47
+
48
+ `Xqsr3::Containers::FrequencyMap` maps each element to a numeric count. It
49
+ includes `Enumerable`, and its `count` method returns the total number of
50
+ observations, while `size` returns the number of distinct elements.
51
+
52
+ ```Ruby
53
+ require 'xqsr3/containers/frequency_map'
54
+
55
+ frequencies = Xqsr3::Containers::FrequencyMap::ByElement[
56
+ 'ruby', 'ruby', 'crystal', 'ruby',
57
+ ]
58
+
59
+ frequencies['ruby'] # => 3
60
+ frequencies['crystal'] # => 1
61
+ frequencies['python'] # => 0
62
+ frequencies.size # => 2
63
+ frequencies.count # => 4
64
+ ```
65
+
66
+ `ByElement[...]` is the convenient constructor when starting with a sequence
67
+ of observations. `FrequencyMap[...]` also accepts a `Hash`, an array of
68
+ `[key, count]` pairs, or an even-length key/count array.
69
+
70
+ The primary mutation methods are:
71
+
72
+ * `push(key, count = 1)` adds to the existing count and removes the key when
73
+ the resulting count is zero;
74
+ * `store(key, count)` replaces the existing count and removes the key when
75
+ `count` is zero;
76
+ * `<< key` records one observation;
77
+ * `delete(key)` removes the key and its contribution to the total;
78
+ * `merge` and `merge!` combine counts for duplicate keys.
79
+
80
+ Counts must be integers. A push that would make an individual count negative
81
+ raises `RangeError`; invalid count values raise `TypeError`.
82
+
83
+ Useful queries include `each`, `each_by_key`, `each_by_frequency`, `fetch`,
84
+ `has_key?`, `has_value?`, `key`, `keys`, `values`, `to_a`, and `to_h`.
85
+ Indexing an absent key returns zero, whereas `fetch` can return a supplied
86
+ default, invoke a block, or raise `KeyError`.
87
+
88
+
89
+ ## `MultiMap`
90
+
91
+ `Xqsr3::Containers::MultiMap` maps each key to an array of values. It includes
92
+ `Enumerable`, and its `size` is the number of keys while `count` is the total
93
+ number of stored values.
94
+
95
+ ```Ruby
96
+ require 'xqsr3/containers/multi_map'
97
+
98
+ groups = Xqsr3::Containers::MultiMap.new
99
+ groups.push :ruby, 'MRI', 'JRuby'
100
+ groups.push :ruby, 'TruffleRuby'
101
+ groups.push :python, 'CPython'
102
+
103
+ groups[:ruby] # => ['MRI', 'JRuby', 'TruffleRuby']
104
+ groups[:python] # => ['CPython']
105
+ groups.size # => 2
106
+ groups.count # => 4
107
+ ```
108
+
109
+ Pushing a key with no values still creates the key and associates it with an
110
+ empty array. Indexing an absent key returns `nil`, which differs from an
111
+ existing key whose value array is empty.
112
+
113
+ The primary mutation methods are:
114
+
115
+ * `push(key, *values)` appends values to the key's existing array;
116
+ * `store(key, *values)` replaces the key's value array;
117
+ * `delete(key)` removes all values mapped to the key;
118
+ * `multi_merge` and `multi_merge!` concatenate values for duplicate keys;
119
+ * `strict_merge` and `strict_merge!` replace values for duplicate keys.
120
+
121
+ `MultiMap[...]` accepts a `Hash` whose values are arrays, an array of
122
+ `[key, value, ...]` entries, or multiple such entries. `each` yields one
123
+ `[key, value]` pair for every stored value. Use `each_unflattened` when the
124
+ value array must be yielded as a whole.
125
+
126
+
127
+ ## Shared conventions
128
+
129
+ Both containers provide `assoc`, `clear`, `delete`, `each_key`, `fetch`,
130
+ `flatten`, `has_key?`, `keys`, `length`, `shift`, `size`, `to_a`, `to_hash`,
131
+ and `values`-style operations. Both support `dup`, equality comparison with
132
+ their corresponding `Hash` representation, and enumerator-returning methods
133
+ when no block is supplied.
134
+
135
+ The containers retain insertion order through their underlying Ruby hashes.
136
+ Methods explicitly described as sorted—`FrequencyMap#each_by_key` and
137
+ `FrequencyMap#each_by_frequency`—perform ordering work at enumeration time.
138
+
139
+ For executable behavioural examples, see the unit tests in
140
+ `test/unit/containers/`.
141
+
142
+
143
+ <!-- ########################### end of file ########################### -->