optionoids 0.1.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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +34 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/README.md +335 -0
- data/Rakefile +10 -0
- data/lib/hash_expecting.rb +16 -0
- data/lib/optionoids/checker.rb +254 -0
- data/lib/optionoids/errors.rb +132 -0
- data/lib/optionoids/version.rb +5 -0
- data/lib/optionoids.rb +4 -0
- data/sig/optionoids.rbs +4 -0
- metadata +83 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 000744ac00ebed3c85f06c226f38081e42a8a9c5f4bfb8954eea52b6da6c1a90
|
4
|
+
data.tar.gz: 771c45f7822b2c74ade3fc89ce8467a910dee5e64736bd2eaad33ca31fe64cef
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 46d34d1c17906cb257b207394611ecff3bb37756e30f554c301b6cffb4b603d0cfde79e24299fbe5d68e90dd41060540133c9c27f75121bc14adac885a3f1a86
|
7
|
+
data.tar.gz: f300123a79410fca5c3e0fa24db59828604980a9f1087bc181c063fd37996873d0b2655786c7c8fbee8327c53336c3e49767c64a564d145d63b9a23e21c37e76
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
plugins:
|
2
|
+
- rubocop-rake
|
3
|
+
- rubocop-rspec
|
4
|
+
|
5
|
+
AllCops:
|
6
|
+
TargetRubyVersion: 3.0
|
7
|
+
SuggestExtensions: false
|
8
|
+
NewCops: enable
|
9
|
+
Exclude:
|
10
|
+
- "bin/**/*"
|
11
|
+
|
12
|
+
Style/StringLiteralsInInterpolation:
|
13
|
+
EnforcedStyle: double_quotes
|
14
|
+
|
15
|
+
Metrics/ClassLength:
|
16
|
+
Max: 150
|
17
|
+
|
18
|
+
Metrics/BlockLength:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
Metrics/AbcSize:
|
22
|
+
Exclude:
|
23
|
+
- "spec/**/*"
|
24
|
+
|
25
|
+
Metrics/CyclomaticComplexity:
|
26
|
+
Exclude:
|
27
|
+
- "spec/**/*"
|
28
|
+
|
29
|
+
Metrics/MethodLength:
|
30
|
+
Exclude:
|
31
|
+
- "spec/**/*"
|
32
|
+
|
33
|
+
RSpec/MultipleExpectations:
|
34
|
+
Max: 2
|
data/CHANGELOG.md
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our
|
6
|
+
community a harassment-free experience for everyone, regardless of age, body
|
7
|
+
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
8
|
+
identity and expression, level of experience, education, socio-economic status,
|
9
|
+
nationality, personal appearance, race, caste, color, religion, or sexual
|
10
|
+
identity and orientation.
|
11
|
+
|
12
|
+
We pledge to act and interact in ways that contribute to an open, welcoming,
|
13
|
+
diverse, inclusive, and healthy community.
|
14
|
+
|
15
|
+
## Our Standards
|
16
|
+
|
17
|
+
Examples of behavior that contributes to a positive environment for our
|
18
|
+
community include:
|
19
|
+
|
20
|
+
* Demonstrating empathy and kindness toward other people
|
21
|
+
* Being respectful of differing opinions, viewpoints, and experiences
|
22
|
+
* Giving and gracefully accepting constructive feedback
|
23
|
+
* Accepting responsibility and apologizing to those affected by our mistakes,
|
24
|
+
and learning from the experience
|
25
|
+
* Focusing on what is best not just for us as individuals, but for the overall
|
26
|
+
community
|
27
|
+
|
28
|
+
Examples of unacceptable behavior include:
|
29
|
+
|
30
|
+
* The use of sexualized language or imagery, and sexual attention or advances of
|
31
|
+
any kind
|
32
|
+
* Trolling, insulting or derogatory comments, and personal or political attacks
|
33
|
+
* Public or private harassment
|
34
|
+
* Publishing others' private information, such as a physical or email address,
|
35
|
+
without their explicit permission
|
36
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
37
|
+
professional setting
|
38
|
+
|
39
|
+
## Enforcement Responsibilities
|
40
|
+
|
41
|
+
Community leaders are responsible for clarifying and enforcing our standards of
|
42
|
+
acceptable behavior and will take appropriate and fair corrective action in
|
43
|
+
response to any behavior that they deem inappropriate, threatening, offensive,
|
44
|
+
or harmful.
|
45
|
+
|
46
|
+
Community leaders have the right and responsibility to remove, edit, or reject
|
47
|
+
comments, commits, code, wiki edits, issues, and other contributions that are
|
48
|
+
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
49
|
+
decisions when appropriate.
|
50
|
+
|
51
|
+
## Scope
|
52
|
+
|
53
|
+
This Code of Conduct applies within all community spaces, and also applies when
|
54
|
+
an individual is officially representing the community in public spaces.
|
55
|
+
Examples of representing our community include using an official email address,
|
56
|
+
posting via an official social media account, or acting as an appointed
|
57
|
+
representative at an online or offline event.
|
58
|
+
|
59
|
+
## Enforcement
|
60
|
+
|
61
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
62
|
+
reported to the community leaders responsible for enforcement at
|
63
|
+
[INSERT CONTACT METHOD].
|
64
|
+
All complaints will be reviewed and investigated promptly and fairly.
|
65
|
+
|
66
|
+
All community leaders are obligated to respect the privacy and security of the
|
67
|
+
reporter of any incident.
|
68
|
+
|
69
|
+
## Enforcement Guidelines
|
70
|
+
|
71
|
+
Community leaders will follow these Community Impact Guidelines in determining
|
72
|
+
the consequences for any action they deem in violation of this Code of Conduct:
|
73
|
+
|
74
|
+
### 1. Correction
|
75
|
+
|
76
|
+
**Community Impact**: Use of inappropriate language or other behavior deemed
|
77
|
+
unprofessional or unwelcome in the community.
|
78
|
+
|
79
|
+
**Consequence**: A private, written warning from community leaders, providing
|
80
|
+
clarity around the nature of the violation and an explanation of why the
|
81
|
+
behavior was inappropriate. A public apology may be requested.
|
82
|
+
|
83
|
+
### 2. Warning
|
84
|
+
|
85
|
+
**Community Impact**: A violation through a single incident or series of
|
86
|
+
actions.
|
87
|
+
|
88
|
+
**Consequence**: A warning with consequences for continued behavior. No
|
89
|
+
interaction with the people involved, including unsolicited interaction with
|
90
|
+
those enforcing the Code of Conduct, for a specified period of time. This
|
91
|
+
includes avoiding interactions in community spaces as well as external channels
|
92
|
+
like social media. Violating these terms may lead to a temporary or permanent
|
93
|
+
ban.
|
94
|
+
|
95
|
+
### 3. Temporary Ban
|
96
|
+
|
97
|
+
**Community Impact**: A serious violation of community standards, including
|
98
|
+
sustained inappropriate behavior.
|
99
|
+
|
100
|
+
**Consequence**: A temporary ban from any sort of interaction or public
|
101
|
+
communication with the community for a specified period of time. No public or
|
102
|
+
private interaction with the people involved, including unsolicited interaction
|
103
|
+
with those enforcing the Code of Conduct, is allowed during this period.
|
104
|
+
Violating these terms may lead to a permanent ban.
|
105
|
+
|
106
|
+
### 4. Permanent Ban
|
107
|
+
|
108
|
+
**Community Impact**: Demonstrating a pattern of violation of community
|
109
|
+
standards, including sustained inappropriate behavior, harassment of an
|
110
|
+
individual, or aggression toward or disparagement of classes of individuals.
|
111
|
+
|
112
|
+
**Consequence**: A permanent ban from any sort of public interaction within the
|
113
|
+
community.
|
114
|
+
|
115
|
+
## Attribution
|
116
|
+
|
117
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
118
|
+
version 2.1, available at
|
119
|
+
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
120
|
+
|
121
|
+
Community Impact Guidelines were inspired by
|
122
|
+
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
123
|
+
|
124
|
+
For answers to common questions about this code of conduct, see the FAQ at
|
125
|
+
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
126
|
+
[https://www.contributor-covenant.org/translations][translations].
|
127
|
+
|
128
|
+
[homepage]: https://www.contributor-covenant.org
|
129
|
+
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
130
|
+
[Mozilla CoC]: https://github.com/mozilla/diversity
|
131
|
+
[FAQ]: https://www.contributor-covenant.org/faq
|
132
|
+
[translations]: https://www.contributor-covenant.org/translations
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2025 drewthorp
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,335 @@
|
|
1
|
+
# Optionoids
|
2
|
+
_(Terrible name, I know)_
|
3
|
+
|
4
|
+
Optionoids provides a simple, flexible, and concise method of validating option hashes passed to methods. Failures of validation can either raise an error or return an array of the errors. Checks can be chained together to create complex validations, and can be used to validate presence, population, types, and counts.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Install the gem and add to the application's Gemfile by executing:
|
9
|
+
|
10
|
+
$ bundle add optionoids
|
11
|
+
|
12
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
13
|
+
|
14
|
+
$ gem install optionoids
|
15
|
+
|
16
|
+
## Usage
|
17
|
+
|
18
|
+
Optionoids provides two methods on the Hash class. The `expecting` method is used for hard validations that will raise an error if the validation fails, while the `checking` method is used for soft validations that will provide an array of errors if the validation fails.
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
require 'optionoids'
|
22
|
+
|
23
|
+
class MyClass
|
24
|
+
def my_method(name, options = {})
|
25
|
+
expecting = options.expecting.with_params(name: name)
|
26
|
+
.only_these(%i[name address age])
|
27
|
+
expecting.that(%i[name address]).required.of_type(String)
|
28
|
+
.and.that(:age).of_type(Integer)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
## Initialization
|
34
|
+
|
35
|
+
To start a hard validation, call `expecting` on the hash you want to validate. This will return an instance of `Optionoids::Checker`.
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
options = { name: 'John', age: 30 }
|
39
|
+
checker = options.expecting
|
40
|
+
```
|
41
|
+
|
42
|
+
To start a soft validation, call `checking` on the hash you want to validate. This will return an instance of `Optionoids::Checker`.
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
options = { name: 'John', age: 30 }
|
46
|
+
checker = options.checking
|
47
|
+
```
|
48
|
+
|
49
|
+
Both methods accept a `keys:` argument to specify an initial key filtering state (see below). The keys argument can be a single key or an array of keys.
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
options = { name: 'John', age: 30, email: 'bob@foo.com' }
|
53
|
+
checker = options.expecting(keys: :name)
|
54
|
+
# or
|
55
|
+
checker = options.checking(keys: [:name, :age])
|
56
|
+
```
|
57
|
+
|
58
|
+
### Additional Parameters
|
59
|
+
|
60
|
+
In addition to the pairs in the hash, additional pairs can be added for validation with the `with_params` method.
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
options.expecting.with_params(name: 'John', age: 30)
|
64
|
+
```
|
65
|
+
|
66
|
+
## Filtering
|
67
|
+
|
68
|
+
The key/value pairs that a check will be performed on can be filtered to only include certain keys. Once a filter is set, all subsequent checks will only be performed on the keys that are in the filter until the filter is altered or cleared. By default, there is no filter set, and all keys in the hash will be checked. An initial filter can be set when the checker is created by passing a `keys:` argument to the `expecting` or `checking` methods.
|
69
|
+
|
70
|
+
### `that(keys)` Method
|
71
|
+
|
72
|
+
Sets a set of keys that subsequent checks will be performed on.
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
checker = options.expecting.that(%i[name address age])
|
76
|
+
checker.keys # => [:name, :address, :age]
|
77
|
+
```
|
78
|
+
|
79
|
+
### `plus(keys)` Method
|
80
|
+
|
81
|
+
Adds keys to the current filter.
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
checker = options.expecting.that(%i[name address]).plus(:age)
|
85
|
+
checker.keys # => [:name, :address, :age]
|
86
|
+
```
|
87
|
+
|
88
|
+
### `minus(keys)` Method
|
89
|
+
|
90
|
+
Removes keys from the current filter.
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
checker = options.expecting.that(%i[name address age]).minus(:address)
|
94
|
+
checker.keys # => [:name, :age]
|
95
|
+
```
|
96
|
+
|
97
|
+
### `and` Method
|
98
|
+
|
99
|
+
Clears the current filter.
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
expecting = options.expecting.that(%i[name address age])
|
103
|
+
expecting.that(:name).required.and.that(:age).populated
|
104
|
+
```
|
105
|
+
|
106
|
+
Alias: _`all`_
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
expecting = options.expecting.that(%i[name address age])
|
110
|
+
expecting.that(:name).required
|
111
|
+
expecting.all.populated
|
112
|
+
```
|
113
|
+
|
114
|
+
## Checks
|
115
|
+
|
116
|
+
Checks are methods that can be chained together to perform validations on the keys and values in the options hash. The checks can be used to validate presence, population, types, and counts. Depending on how the checker was initialized, the checks will either raise an error or add to an array of errors (See `#errors` for accessing the array).
|
117
|
+
|
118
|
+
### `only_these(keys)` Method
|
119
|
+
|
120
|
+
Checks that only the keys provided are present in the options hash. If any other keys are present, an error will be raised or added to the errors array.
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
expecting = options.expecting.only_these(%i[name address age])
|
124
|
+
```
|
125
|
+
|
126
|
+
Error raised/logged: _Optionoids::Errors::UnexpectedKeys_
|
127
|
+
|
128
|
+
### `exist` Method
|
129
|
+
|
130
|
+
Checks that all the currently set filter keys are present in the current option Hash. If there are no entries in the current option Hash, an error is raised. If any of the keys are missing, an error is raised.
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
expecting = options.expecting.that(:name).exist
|
134
|
+
```
|
135
|
+
|
136
|
+
Errors raised/logged:
|
137
|
+
- Empty hash - _Optionoids::Errors::RequiredDataUnavailable_
|
138
|
+
- Missing keys - _Optionoids::Errors::MissingKeys_
|
139
|
+
|
140
|
+
### `populated` Method
|
141
|
+
|
142
|
+
Checks that all the filter keys in the hash are not nil or empty. If any of the keys are nil or empty, an error is raised or added to the errors array. It does not error if the keys do not exist in the hash.
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
expecting = options.expecting.that(:name).populated
|
146
|
+
```
|
147
|
+
|
148
|
+
Error raised/logged: _Optionoids::Errors::UnexpectedBlankValue_
|
149
|
+
|
150
|
+
Alias: _`all_populated`_
|
151
|
+
|
152
|
+
### `blank` Method
|
153
|
+
|
154
|
+
Checks that all the filter keys in the hash are nil or empty. If any of the keys are not nil or empty, an error is raised or added to the errors array. It does not error if the keys do not exist in the hash.
|
155
|
+
|
156
|
+
```ruby
|
157
|
+
expecting = options.expecting.that(:name).blank
|
158
|
+
```
|
159
|
+
|
160
|
+
Error raised/logged: _Optionoids::Errors::UnexpectedPopulatedValue_
|
161
|
+
|
162
|
+
Alias: _`all_blank`_
|
163
|
+
|
164
|
+
### `not_nil_values` Method
|
165
|
+
|
166
|
+
Checks that all the filter keys in the hash are not nil. If any of the keys are nil, an error is raised or added to the errors array. It does not error if the keys do not exist in the hash.
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
expecting = options.expecting.that(:name).not_nil_values
|
170
|
+
```
|
171
|
+
|
172
|
+
Error raised/logged: _Optionoids::Errors::UnexpectedNilValue_
|
173
|
+
|
174
|
+
### `nil_values` Method
|
175
|
+
|
176
|
+
Checks that all the filter keys in the hash are nil. If any of the keys are not nil, an error is raised or added to the errors array. It does not error if the keys do not exist in the hash.
|
177
|
+
|
178
|
+
```ruby
|
179
|
+
expecting = options.expecting.that(:name).nil_values
|
180
|
+
```
|
181
|
+
|
182
|
+
Error raised/logged: _Optionoids::Errors::UnexpectedNotNilValue_
|
183
|
+
|
184
|
+
### `one_or_none` Method
|
185
|
+
|
186
|
+
Checks that at most one of the filter keys in the hash are present. If more than one of the keys are present, an error is raised or added to the errors array. It does not consider values, only the presence of the keys.
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
expecting = options.expecting.that(%i[name age]).one_or_none
|
190
|
+
```
|
191
|
+
|
192
|
+
Error raised/logged: _Optionoids::Errors::UnexpectedMultipleKeys_
|
193
|
+
|
194
|
+
### `just_one` Method
|
195
|
+
|
196
|
+
Checks that exactly one of the filter keys in the hash are present. If none or more than one of the keys are present, an error is raised or added to the errors array. It does not consider values, only the presence of the keys.
|
197
|
+
|
198
|
+
```ruby
|
199
|
+
expecting = options.expecting.that(%i[name age]).just_one
|
200
|
+
```
|
201
|
+
|
202
|
+
Errors raised/logged:
|
203
|
+
- Empty hash - _Optionoids::Errors::RequiredDataUnavailable_
|
204
|
+
- None or more than one keys - _Optionoids::Errors::UnexpectedMultipleKeys_
|
205
|
+
|
206
|
+
### `one_or_more` Method
|
207
|
+
|
208
|
+
Checks that at least one of the filter keys in the hash are present. If none of the keys are present, an error is raised or added to the errors array. It does not consider values, only the presence of the keys.
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
expecting = options.expecting.that(%i[name age]).one_or_more
|
212
|
+
```
|
213
|
+
|
214
|
+
Error raised/logged: _Optionoids::Errors::ExpectedMultipleKeys_
|
215
|
+
|
216
|
+
### `of_types(types)` Method
|
217
|
+
|
218
|
+
Checks that the values of the filter keys in the hash are of the types provided. If any of the values are not of the expected type, an error is raised or added to the errors array. It does not error if the keys do not exist in the hash or if the value is nil.
|
219
|
+
|
220
|
+
```ruby
|
221
|
+
expecting = options.expecting.that(:name).of_types(String, Symbol)
|
222
|
+
```
|
223
|
+
|
224
|
+
Error raised/logged: _Optionoids::Errors::UnexpectedValueType_
|
225
|
+
|
226
|
+
Alias: _`of_type`_, _`types`_, _`type`_
|
227
|
+
|
228
|
+
### `possible_values(variants)` Method
|
229
|
+
|
230
|
+
Checks that the values of the filter keys in the hash are one of the possible values provided. If any of the values are not one of the possible values, an error is raised or added to the errors array. It does not error if the keys do not exist in the hash or if the value is nil.
|
231
|
+
|
232
|
+
```ruby
|
233
|
+
expecting = options.expecting.that(:name).possible_values('John', 'Jane', 'Doe')
|
234
|
+
```
|
235
|
+
|
236
|
+
Error raised/logged: _Optionoids::Errors::UnexpectedValueVariant_
|
237
|
+
|
238
|
+
## Convenience Methods
|
239
|
+
|
240
|
+
The `Optionoids::Checker` class provides several convenience methods to make it easier to perform common checks. These methods are available on both hard and soft checkers.
|
241
|
+
|
242
|
+
### `identifier` Method
|
243
|
+
|
244
|
+
Checks that the value of the filter key is a valid identifier. A valid identifier is a populated String or Symbol.
|
245
|
+
|
246
|
+
```ruby
|
247
|
+
expecting = options.expecting.that(:name).identifier
|
248
|
+
```
|
249
|
+
|
250
|
+
Errors raised/logged:
|
251
|
+
- If the wrong type: _Optionoids::Errors::UnexpectedValueType_
|
252
|
+
- If the value is nil or empty: _Optionoids::Errors::UnexpectedBlankValue_
|
253
|
+
|
254
|
+
### `flag` Method
|
255
|
+
|
256
|
+
Checks that the value of the filter key is a populated boolean. A boolean is either `true` or `false`.
|
257
|
+
|
258
|
+
```ruby
|
259
|
+
expecting = options.expecting.that(:active).flag
|
260
|
+
```
|
261
|
+
|
262
|
+
Errors raised/logged:
|
263
|
+
- If the wrong type: _Optionoids::Errors::UnexpectedValueType_
|
264
|
+
- If the value is nil or empty: _Optionoids::Errors::UnexpectedBlankValue_
|
265
|
+
|
266
|
+
### `required` Method
|
267
|
+
|
268
|
+
Checks that the filter key is present and populated in the options hash.
|
269
|
+
|
270
|
+
```ruby
|
271
|
+
expecting = options.expecting.that(:name).required
|
272
|
+
```
|
273
|
+
|
274
|
+
Errors raised/logged:
|
275
|
+
- If the hash is empty: _Optionoids::Errors::RequiredDataUnavailable_
|
276
|
+
- Not present: _Optionoids::Errors::MissingKeys_
|
277
|
+
- If the value is nil or empty: _Optionoids::Errors::UnexpectedBlankValue_
|
278
|
+
|
279
|
+
## Soft Errors
|
280
|
+
|
281
|
+
If the checker was initialized with `checking`, the errors will be collected in an array. You can access the errors using the `errors` method. A predicate method `failed?` is also available to check if there are any errors.
|
282
|
+
|
283
|
+
```ruby
|
284
|
+
checker = options.checking.that(:name).required
|
285
|
+
checker.errors # => ["Missing keys: name", "Unexpected blank value for key: name"]
|
286
|
+
checker.failed? # => true
|
287
|
+
```
|
288
|
+
|
289
|
+
## Data Access / Debugging
|
290
|
+
|
291
|
+
### `current_options` Method
|
292
|
+
|
293
|
+
Returns the current filtered options Hash that is being checked.
|
294
|
+
|
295
|
+
```ruby
|
296
|
+
checker = options.expecting.that(:name).current_options
|
297
|
+
# => { name: 'John' }
|
298
|
+
```
|
299
|
+
|
300
|
+
### `global_options` Method
|
301
|
+
|
302
|
+
Returns the original options Hash that was passed to the checker. Options added with `with_params` are included in this hash.
|
303
|
+
|
304
|
+
```ruby
|
305
|
+
checker = options.expecting.with_params(name: 'John').global_options
|
306
|
+
# => { name: 'John', age: 30 }
|
307
|
+
```
|
308
|
+
|
309
|
+
### `keys` Method
|
310
|
+
|
311
|
+
Returns the keys that are currently being checked. This is useful to see which keys are in the current filter.
|
312
|
+
|
313
|
+
```ruby
|
314
|
+
checker = options.expecting.that(:name, :age).keys
|
315
|
+
# => [:name, :age]
|
316
|
+
```
|
317
|
+
|
318
|
+
## Future Enhancements
|
319
|
+
|
320
|
+
- Add support for regex checks on values.
|
321
|
+
- Add common checks using regex (e.g., email, URL).
|
322
|
+
- Add support for range checks on numeric & date values.
|
323
|
+
- Implement a similar API for cleaning the options hash.
|
324
|
+
|
325
|
+
## Contributing
|
326
|
+
|
327
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/optionoids. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/optionoids/blob/main/CODE_OF_CONDUCT.md).
|
328
|
+
|
329
|
+
## License
|
330
|
+
|
331
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
332
|
+
|
333
|
+
## Code of Conduct
|
334
|
+
|
335
|
+
Everyone interacting in the Optionoids project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/optionoids/blob/main/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Extends the Hash class to support optionoid option parsing.
|
4
|
+
class Hash
|
5
|
+
# Perform hard checking on the Hash. Hard checking will raise an error if the Hash does not
|
6
|
+
# conform to the expectations.
|
7
|
+
def expecting(keys = nil)
|
8
|
+
Optionoids::Checker.new(self, keys: keys)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Perform soft checking on the Hash. Soft checking will not raise an error. Errors are logged
|
12
|
+
# and can be checked with errors and failed? methods.
|
13
|
+
def checking(keys = nil)
|
14
|
+
Optionoids::Checker.new(self, keys: keys, hard: false)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,254 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/object/blank'
|
4
|
+
require 'hash_expecting'
|
5
|
+
require 'optionoids/errors'
|
6
|
+
|
7
|
+
module Optionoids
|
8
|
+
# Class to wrap an options Hash and perform checks on the keys and values. All method return
|
9
|
+
# the same instance of the Checker, allowing for method chaining.
|
10
|
+
class Checker
|
11
|
+
attr_reader :hard, :keys
|
12
|
+
|
13
|
+
# @param options [Hash] The options Hash to check against.
|
14
|
+
# @param keys [Array<String, Symbol>] The keys to initially check in the options Hash. If nil,
|
15
|
+
# all keys are checked.
|
16
|
+
def initialize(options, keys: nil, hard: true)
|
17
|
+
@options = options
|
18
|
+
@keys = [keys].flatten.compact
|
19
|
+
@params = {}
|
20
|
+
@hard = hard
|
21
|
+
clip_options
|
22
|
+
end
|
23
|
+
|
24
|
+
# A set of additional options to check against. This is useful for checking other none optional
|
25
|
+
# parameters that are not part of the options Hash.
|
26
|
+
def with_params(params)
|
27
|
+
@params = params.is_a?(Hash) ? params : params.to_h
|
28
|
+
clip_options
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the current 'filtered' option Hash.
|
33
|
+
def current_options
|
34
|
+
@clipped_options
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns the 'unfiltered' options Hash.
|
38
|
+
def global_options
|
39
|
+
@options.merge(@params)
|
40
|
+
end
|
41
|
+
|
42
|
+
## FILTERING ##
|
43
|
+
|
44
|
+
# Removes all option Hash filtering.
|
45
|
+
def and
|
46
|
+
@keys = []
|
47
|
+
clip_options
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
alias all and
|
52
|
+
|
53
|
+
# Add a key set filter to the current option Hash. The keys provided do not have to exist in the
|
54
|
+
# option Hash, but only those that do will be checked. Filters are not cumulative, so calling
|
55
|
+
# this method will replace any previous key filters.
|
56
|
+
def that(*keys)
|
57
|
+
@keys = keys.flatten.compact
|
58
|
+
clip_options
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
# Removes the keys from the current option Hash filter. No error will be raised if the given
|
63
|
+
# keys are not present in the current option Hash.
|
64
|
+
def minus(*keys)
|
65
|
+
@keys -= keys
|
66
|
+
clip_options
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
# Adds the given keys to the current option Hash filter. No error will be raised if the given
|
71
|
+
# keys are not present in the current option Hash.
|
72
|
+
def plus(*keys)
|
73
|
+
@keys |= keys.flatten.compact.uniq
|
74
|
+
clip_options
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
## KEY PRESENCE CHECKS ##
|
79
|
+
|
80
|
+
# Checks that the current option Hash contains only the given keys. If any unexpected keys are
|
81
|
+
# present, an error is raised. If no keys are given, the current option Hash is not checked.
|
82
|
+
# Error: Optionoids::Errors::UnexpectedKeys
|
83
|
+
def only_these(keys)
|
84
|
+
unexpected_keys = @clipped_options.keys - [keys].flatten
|
85
|
+
_error_or_log(Errors::UnexpectedKeys.new(nil, keys: unexpected_keys)) if unexpected_keys.any?
|
86
|
+
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
# Checks that all the currently set filter keys are present in the current option Hash. If there
|
91
|
+
# are no entries in the current option Hash an error (Optionoids::Errors::RequiredDataUnavailable)
|
92
|
+
# is raised. If any of the keys are missing, an error (Optionoids::Errors::MissingKeys) is raised.
|
93
|
+
def exist
|
94
|
+
return _error_or_log(Errors::RequiredDataUnavailable.new(nil, check: 'present')) if @keys.empty?
|
95
|
+
|
96
|
+
missing_keys = @keys - @clipped_options.keys
|
97
|
+
return self if missing_keys.empty?
|
98
|
+
|
99
|
+
_error_or_log(Errors::MissingKeys.new(nil, keys: missing_keys))
|
100
|
+
end
|
101
|
+
|
102
|
+
## VALUE POPULATION CHECKS ##
|
103
|
+
|
104
|
+
# Checks that the current option Hash entries all have non-blank values. If any of the values
|
105
|
+
# are blank, an error (Optionoids::Errors::UnexpectedBlankValue) is raised.
|
106
|
+
def populated
|
107
|
+
_error_on_check(:blank?, Errors::UnexpectedBlankValue)
|
108
|
+
self
|
109
|
+
end
|
110
|
+
|
111
|
+
alias all_populated populated
|
112
|
+
|
113
|
+
# Checks that the current option Hash entries all have blank values. If any of the values are
|
114
|
+
# populated, an error (Optionoids::Errors::UnexpectedPopulatedValue) is raised.
|
115
|
+
def blank
|
116
|
+
_error_on_check(:present?, Errors::UnexpectedPopulatedValue)
|
117
|
+
self
|
118
|
+
end
|
119
|
+
|
120
|
+
alias all_blank blank
|
121
|
+
|
122
|
+
# Checks that the current option Hash entries all have non-nil values. If any of the values are
|
123
|
+
# nil, an error (Optionoids::Errors::UnexpectedNilValue) is raised.
|
124
|
+
def not_nil_values
|
125
|
+
_error_on_check(:nil?, Errors::UnexpectedNilValue)
|
126
|
+
self
|
127
|
+
end
|
128
|
+
|
129
|
+
# Checks that the current option Hash entries all have nil values. If any of the values are
|
130
|
+
# not nil, an error (Optionoids::Errors::UnexpectedNonNilValue) is raised.
|
131
|
+
def nil_values
|
132
|
+
failed_keys = @clipped_options.compact.keys
|
133
|
+
return self if failed_keys.empty?
|
134
|
+
|
135
|
+
_error_or_log(Errors::UnexpectedNonNilValue.new(nil, keys: failed_keys))
|
136
|
+
end
|
137
|
+
|
138
|
+
## KEY COUNT CHECKS ##
|
139
|
+
|
140
|
+
# Checks that the current option Hash contains no more that one key. If more than one key id
|
141
|
+
# present, an error (Optionoids::Errors::UnexpectedMultipleKeys) is raised. If there are no keys
|
142
|
+
# present, no error is raised.
|
143
|
+
def one_or_none
|
144
|
+
msg = "Expected a maximum or one key, but found: #{@clipped_options.keys.to_sentence}"
|
145
|
+
_error_or_log(Errors::UnexpectedMultipleKeys.new(msg, keys: @clipped_options.keys)) if @clipped_options.count > 1
|
146
|
+
|
147
|
+
self
|
148
|
+
end
|
149
|
+
|
150
|
+
# Checks that the current option Hash contains exactly one key. If no keys are present, an error
|
151
|
+
# (Optionoids::Errors::RequiredDataUnavailable) is raised. If more than one key is present, an error
|
152
|
+
# (Optionoids::Errors::UnexpectedMultipleKeys) is raised.
|
153
|
+
def just_one
|
154
|
+
_error_or_log(Errors::RequiredDataUnavailable.new(nil, check: 'one_required')) if @clipped_options.empty?
|
155
|
+
return self if @clipped_options.one?
|
156
|
+
|
157
|
+
msg = "Expected exactly one key, but found: #{@clipped_options.keys.to_sentence}"
|
158
|
+
_error_or_log(Errors::UnexpectedMultipleKeys.new(msg, keys: @clipped_options.keys))
|
159
|
+
end
|
160
|
+
|
161
|
+
# Checks that the current option Hash contains one or more keys. If no keys are present, an error
|
162
|
+
# (Optionoids::Errors::ExpectedMultipleKeys).
|
163
|
+
def one_of_more
|
164
|
+
return self if @clipped_options.count >= 1
|
165
|
+
|
166
|
+
_error_or_log(Errors::ExpectedMultipleKeys.new)
|
167
|
+
end
|
168
|
+
|
169
|
+
## TYPE CHECKS ##
|
170
|
+
|
171
|
+
# Checks that the current option Hash entries are of the given types. If any of the values are
|
172
|
+
# not of the given types, an error (Optionoids::Errors::UnexpectedValueType) is raised. 'nil'
|
173
|
+
# values are ignored in the type check.
|
174
|
+
def of_types(*types)
|
175
|
+
pairs = @clipped_options.compact.select { |_k, v| types.none? { |t| v.is_a?(t) } }
|
176
|
+
return self if pairs.empty?
|
177
|
+
|
178
|
+
_error_or_log(Errors::UnexpectedValueType.new(nil, keys: pairs.keys, types: types.map(&:name)))
|
179
|
+
end
|
180
|
+
|
181
|
+
alias of_type of_types
|
182
|
+
alias types of_types
|
183
|
+
alias type of_types
|
184
|
+
|
185
|
+
## VALUE CHECKS ##
|
186
|
+
|
187
|
+
# Checks that the current option Hash entries are one of the given variants. If any of the
|
188
|
+
# values are not one of the given variants, an error (Optionoids::Errors::UnexpectedValueVariant).
|
189
|
+
# If a value is nil it is ignored in the check.
|
190
|
+
def possible_values(variants)
|
191
|
+
pairs = @clipped_options.compact.select { |_k, v| variants.none? { |variant| v == variant } }
|
192
|
+
return self if pairs.empty?
|
193
|
+
|
194
|
+
_error_or_log(Errors::UnexpectedValueVariant.new(nil, keys: pairs.keys, variants: variants))
|
195
|
+
end
|
196
|
+
|
197
|
+
## COMPOSITE CHECKS ##
|
198
|
+
|
199
|
+
# Checks that the current option Hash entries are usable as identifiers. This means that the
|
200
|
+
# values are either Strings or Symbols and are not blank.
|
201
|
+
def identifier
|
202
|
+
of_type(String, Symbol).populated
|
203
|
+
end
|
204
|
+
|
205
|
+
# Checks that the current option Hash entries are usable as flags. This means that the values
|
206
|
+
# are either TrueClass or FalseClass and are not blank.
|
207
|
+
def flag
|
208
|
+
# Populated check mist use not nil because false is never 'present?'
|
209
|
+
of_type(TrueClass, FalseClass).not_nil_values
|
210
|
+
end
|
211
|
+
|
212
|
+
# Checks that the current option Hash entries ate both present and populated.
|
213
|
+
def required
|
214
|
+
exist.populated
|
215
|
+
end
|
216
|
+
|
217
|
+
## SOFT ERROR HANDLING ##
|
218
|
+
|
219
|
+
def errors
|
220
|
+
@errors ||= []
|
221
|
+
end
|
222
|
+
|
223
|
+
def failed?
|
224
|
+
errors.any?
|
225
|
+
end
|
226
|
+
|
227
|
+
private
|
228
|
+
|
229
|
+
def _error_or_log(error)
|
230
|
+
raise error if @hard
|
231
|
+
|
232
|
+
errors << error
|
233
|
+
self
|
234
|
+
end
|
235
|
+
|
236
|
+
def _error_on_check(check, error_class)
|
237
|
+
failed_keys = _keys_for_check(check)
|
238
|
+
return if failed_keys.empty?
|
239
|
+
|
240
|
+
_error_or_log(error_class.new(nil, keys: failed_keys))
|
241
|
+
end
|
242
|
+
|
243
|
+
def _keys_for_check(check)
|
244
|
+
@clipped_options.select { |_k, v| v.send(check) }.to_h.keys
|
245
|
+
end
|
246
|
+
|
247
|
+
def clip_options
|
248
|
+
@clipped_options = @options.merge(@params)
|
249
|
+
return if @keys.empty?
|
250
|
+
|
251
|
+
@clipped_options = @clipped_options.slice(*@keys)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/array/conversions'
|
4
|
+
|
5
|
+
module Optionoids
|
6
|
+
# Errors module contains custom error classes for the Optionoids library.
|
7
|
+
module Errors
|
8
|
+
# Custom error class to indicate that a checker requires keys in #keys or key/values in the
|
9
|
+
# current option Hash, but none were provided.
|
10
|
+
class RequiredDataUnavailable < StandardError
|
11
|
+
attr_reader :check
|
12
|
+
|
13
|
+
def initialize(message = nil, check: nil)
|
14
|
+
msg = message || "Required data is unavailable for the check '#{check}'"
|
15
|
+
@check = check
|
16
|
+
super(msg)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Custom error class to indicate the expected keys are not present in the current option Hash
|
21
|
+
class MissingKeys < StandardError
|
22
|
+
attr_reader :keys
|
23
|
+
|
24
|
+
def initialize(message = nil, keys: [])
|
25
|
+
@keys = keys
|
26
|
+
msg = message || "Missing required keys: #{keys.to_sentence}"
|
27
|
+
super(msg)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Custom error class to indicate that unexpected keys are in the current option Hash
|
32
|
+
class UnexpectedKeys < StandardError
|
33
|
+
attr_reader :keys
|
34
|
+
|
35
|
+
def initialize(message = nil, keys: [])
|
36
|
+
@keys = keys
|
37
|
+
msg = message || "Unexpected keys found: #{keys.to_sentence}"
|
38
|
+
super(msg)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Custom error class to indicate that some values ate unexpectedly blank or nil
|
43
|
+
class UnexpectedBlankValue < StandardError
|
44
|
+
attr_reader :keys
|
45
|
+
|
46
|
+
def initialize(message = nil, keys: [])
|
47
|
+
@keys = keys
|
48
|
+
msg = message || "Unexpected blank values for keys: #{keys.to_sentence}"
|
49
|
+
super(msg)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Custom error class to indicate that some values are unexpectedly populated (not blank)
|
54
|
+
class UnexpectedPopulatedValue < StandardError
|
55
|
+
attr_reader :keys
|
56
|
+
|
57
|
+
def initialize(message = nil, keys: [])
|
58
|
+
@keys = keys
|
59
|
+
msg = message || "Unexpected populated values for keys: #{keys.to_sentence}"
|
60
|
+
super(msg)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Custom error class to indicate that some values are unexpectedly not nil
|
65
|
+
class UnexpectedNonNilValue < StandardError
|
66
|
+
attr_reader :keys
|
67
|
+
|
68
|
+
def initialize(message = nil, keys: [])
|
69
|
+
@keys = keys
|
70
|
+
msg = message || "Unexpected non-nil values for keys: #{keys.to_sentence}"
|
71
|
+
super(msg)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Custom error class to indicate that some values are unexpectedly nil
|
76
|
+
class UnexpectedNilValue < StandardError
|
77
|
+
attr_reader :keys
|
78
|
+
|
79
|
+
def initialize(message = nil, keys: [])
|
80
|
+
@keys = keys
|
81
|
+
msg = message || "Unexpected nil values for keys: #{keys.to_sentence}"
|
82
|
+
super(msg)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Custom error class to indicate that only one key is expected, but multiple keys are present
|
87
|
+
class UnexpectedMultipleKeys < StandardError
|
88
|
+
attr_reader :keys
|
89
|
+
|
90
|
+
def initialize(message = nil, keys: [])
|
91
|
+
@keys = keys
|
92
|
+
msg = message || "Multiple keys present when only one is expected: #{keys.to_sentence}"
|
93
|
+
super(msg)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Custom error class to indicate that the values for the keys are not of the expected types
|
98
|
+
class UnexpectedValueType < StandardError
|
99
|
+
attr_reader :keys, :types
|
100
|
+
|
101
|
+
def initialize(message = nil, keys: [], types: [])
|
102
|
+
@keys = keys
|
103
|
+
@types = types
|
104
|
+
msg = message || "Unexpected value types for keys: #{keys.to_sentence}. " \
|
105
|
+
"Expected types: #{types.to_sentence}"
|
106
|
+
super(msg)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Custom error class to indicate that a values in the current option Hash are not of the
|
111
|
+
# expected variants
|
112
|
+
class UnexpectedValueVariant < StandardError
|
113
|
+
attr_reader :keys, :variants
|
114
|
+
|
115
|
+
def initialize(message = nil, keys: [], variants: [])
|
116
|
+
@keys = keys
|
117
|
+
@variants = variants
|
118
|
+
msg = message || "Unexpected value variants for keys: #{keys.to_sentence}. " \
|
119
|
+
"Expected variants: #{variants.to_sentence}"
|
120
|
+
super(msg)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Custom error class to indicate that a checker expected multiple keys but none were provided
|
125
|
+
class ExpectedMultipleKeys < StandardError
|
126
|
+
def initialize(message = nil)
|
127
|
+
msg = message || 'Expected multiple keys but none were provided'
|
128
|
+
super(msg)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/lib/optionoids.rb
ADDED
data/sig/optionoids.rbs
ADDED
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: optionoids
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- drewthorp
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-07-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 7.1.0
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '9.0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 7.1.0
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '9.0'
|
33
|
+
description: Optionoids is a Ruby gem designed to provide a simple and flexible way
|
34
|
+
to validate and check the content of option hashes. It allows developers to define
|
35
|
+
checks for required keys, unexpected keys, and value conditions, making it easier
|
36
|
+
to work with configuration options in Ruby applications.
|
37
|
+
email:
|
38
|
+
- gems@fishfur.com
|
39
|
+
executables: []
|
40
|
+
extensions: []
|
41
|
+
extra_rdoc_files: []
|
42
|
+
files:
|
43
|
+
- ".rspec"
|
44
|
+
- ".rubocop.yml"
|
45
|
+
- CHANGELOG.md
|
46
|
+
- CODE_OF_CONDUCT.md
|
47
|
+
- LICENSE.txt
|
48
|
+
- README.md
|
49
|
+
- Rakefile
|
50
|
+
- lib/hash_expecting.rb
|
51
|
+
- lib/optionoids.rb
|
52
|
+
- lib/optionoids/checker.rb
|
53
|
+
- lib/optionoids/errors.rb
|
54
|
+
- lib/optionoids/version.rb
|
55
|
+
- sig/optionoids.rbs
|
56
|
+
homepage: https://github.com/Fish-Fur/optionoids
|
57
|
+
licenses:
|
58
|
+
- MIT
|
59
|
+
metadata:
|
60
|
+
homepage_uri: https://github.com/Fish-Fur/optionoids
|
61
|
+
source_code_uri: https://github.com/Fish-Fur/optionoids
|
62
|
+
changelog_uri: https://github.com/Fish-Fur/optionoids
|
63
|
+
rubygems_mfa_required: 'true'
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options: []
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 3.0.0
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
requirements: []
|
79
|
+
rubygems_version: 3.5.11
|
80
|
+
signing_key:
|
81
|
+
specification_version: 4
|
82
|
+
summary: A Ruby gem for checking content of option hashes.
|
83
|
+
test_files: []
|