exclusive_case 1.0.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 +31 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +42 -0
- data/LICENSE +21 -0
- data/README.md +221 -0
- data/Rakefile +11 -0
- data/exclusive_case.gemspec +33 -0
- data/lib/exclusive_case/case_builder.rb +53 -0
- data/lib/exclusive_case/version.rb +5 -0
- data/lib/exclusive_case.rb +43 -0
- metadata +57 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 3e6f61086cef55f398527daf61d443a704f3ee229292b8c1c018274cafe8500b
|
|
4
|
+
data.tar.gz: b368ec1f93619f448b331d40047e6c0d7793fcf93e8ed130246d08dd81f2ba2d
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a9974d64182c95765dfc1d20737a3d336d8e5cfabeb09d05778f48454fe822503396c75422dcc88100ed6327f04ba7fdf47baab9555e426c8a6135cd8f6616cf
|
|
7
|
+
data.tar.gz: a5c174dcd2af2969127485719096832635d68c855b858ca7bcc9f44b16c27811902b7d2eaf37e2be6fb7705e4e9ab2de65ad46dd5d5bbbbaea9b5145c42e2bf6
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
NewCops: enable
|
|
3
|
+
SuggestExtensions: false
|
|
4
|
+
|
|
5
|
+
plugins:
|
|
6
|
+
- rubocop-rspec
|
|
7
|
+
|
|
8
|
+
Style/StringLiterals:
|
|
9
|
+
Enabled: true
|
|
10
|
+
EnforcedStyle: double_quotes
|
|
11
|
+
|
|
12
|
+
Style/StringLiteralsInInterpolation:
|
|
13
|
+
Enabled: true
|
|
14
|
+
EnforcedStyle: double_quotes
|
|
15
|
+
|
|
16
|
+
Layout/LineLength:
|
|
17
|
+
Max: 120
|
|
18
|
+
|
|
19
|
+
Style/Documentation:
|
|
20
|
+
Enabled: true
|
|
21
|
+
|
|
22
|
+
Metrics/BlockLength:
|
|
23
|
+
Exclude:
|
|
24
|
+
- "spec/**/*"
|
|
25
|
+
|
|
26
|
+
Style/FrozenStringLiteralComment:
|
|
27
|
+
Enabled: true
|
|
28
|
+
|
|
29
|
+
# Disable RSpec/ExampleLength - allow longer test examples for clarity
|
|
30
|
+
RSpec/ExampleLength:
|
|
31
|
+
Enabled: false
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.4.5
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [1.0.0] - 2025-08-02
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **BREAKING**: Optional `of:` parameter for enhanced validation
|
|
14
|
+
- `InvalidCaseError` - raised when cases are not in the allowed `of:` list
|
|
15
|
+
- `MissingCaseError` - raised when not all `of:` values are handled
|
|
16
|
+
- Comprehensive validation ensuring all cases belong to specified list
|
|
17
|
+
- Requirement that all `of:` values must have corresponding cases
|
|
18
|
+
- Full backward compatibility when `of:` parameter is not used
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- **BREAKING**: `exhaustive_case` signature now accepts optional `of:` parameter
|
|
22
|
+
|
|
23
|
+
### Technical
|
|
24
|
+
- 100% test coverage maintained
|
|
25
|
+
- Full RuboCop compliance with comprehensive documentation
|
|
26
|
+
- GitHub Actions CI/CD pipeline with multi-Ruby version testing
|
|
27
|
+
- Automated dependency management via Dependabot
|
|
28
|
+
|
|
29
|
+
## [0.1.0] - 2025-08-02
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
- Initial implementation of `exhaustive_case` method
|
|
33
|
+
- Support for single and multiple value matching in `on` clauses
|
|
34
|
+
- Automatic error raising for unhandled cases via `UnhandledCaseError`
|
|
35
|
+
- Core gem structure and configuration
|
|
36
|
+
- RSpec testing framework setup
|
|
37
|
+
- RuboCop linting configuration
|
|
38
|
+
- Development scripts (`bin/setup`, `bin/console`)
|
|
39
|
+
|
|
40
|
+
[Unreleased]: https://github.com/yourusername/exclusive_case/compare/v1.0.0...HEAD
|
|
41
|
+
[1.0.0]: https://github.com/yourusername/exclusive_case/releases/tag/v1.0.0
|
|
42
|
+
[0.1.0]: https://github.com/yourusername/exclusive_case/releases/tag/v0.1.0
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ajay Sharma
|
|
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 all
|
|
13
|
+
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 THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Exclusive Case
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/rb/exclusive_case)
|
|
4
|
+
[](https://github.com/ajsharma/exclusive_case/actions/workflows/ci.yml)
|
|
5
|
+
|
|
6
|
+
Exhaustive case statements for Ruby to prevent bugs from unhandled cases when new values are added to a system.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
Add this line to your application's Gemfile:
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
gem 'exclusive_case'
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
And then execute:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
$ bundle install
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Or install it yourself as:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
$ gem install exclusive_case
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
Include the module to get access to the `exhaustive_case` method:
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
require 'exclusive_case'
|
|
34
|
+
include ExclusiveCase
|
|
35
|
+
|
|
36
|
+
# Now you can use exhaustive_case
|
|
37
|
+
result = exhaustive_case user_status do
|
|
38
|
+
on(:active) { "User is active" }
|
|
39
|
+
on(:inactive) { "User is inactive" }
|
|
40
|
+
on(:pending) { "User is pending approval" }
|
|
41
|
+
end
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
or to avoid globally adding the module, it can be included in a class:
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
require 'exclusive_case'
|
|
48
|
+
|
|
49
|
+
class UserRenderer
|
|
50
|
+
include ExclusiveCase
|
|
51
|
+
|
|
52
|
+
def handle_status(user_status)
|
|
53
|
+
# Now you can use exhaustive_case
|
|
54
|
+
result = exhaustive_case user_status do
|
|
55
|
+
on(:active) { "User is active" }
|
|
56
|
+
on(:inactive) { "User is inactive" }
|
|
57
|
+
on(:pending) { "User is pending approval" }
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## The problem
|
|
64
|
+
|
|
65
|
+
If/else statements can easily lead to mistake flows when introducing new cases across the systems.
|
|
66
|
+
|
|
67
|
+
For instance, with a constant set of inputs `['A', 'B', 'C']`:
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
if letter == 'A'
|
|
71
|
+
// handle a
|
|
72
|
+
elsif letter == 'B'
|
|
73
|
+
// handle b
|
|
74
|
+
else // implicit c
|
|
75
|
+
// handle c
|
|
76
|
+
end
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
However, if a new entry `D` is introduced to the system, now:
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
if letter == 'A'
|
|
83
|
+
// code to handle a (whoops!)
|
|
84
|
+
elsif letter == 'B'
|
|
85
|
+
// code to handle b (whoops!)
|
|
86
|
+
else // implicit c and d
|
|
87
|
+
// code to handle c (whoops!)
|
|
88
|
+
end
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
This kind of code does not reveal itself in tests, providing no feedback to the developer that that a new flow is needed.
|
|
92
|
+
|
|
93
|
+
We can address this with a more explicit initial switch:
|
|
94
|
+
|
|
95
|
+
```ruby
|
|
96
|
+
if letter == 'A'
|
|
97
|
+
// handle a
|
|
98
|
+
elsif letter == 'B'
|
|
99
|
+
// handle b
|
|
100
|
+
elsif letter == 'C'
|
|
101
|
+
// handle c
|
|
102
|
+
else
|
|
103
|
+
raise "Unknown letter #{letter}"
|
|
104
|
+
end
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Now, a new letter would trigger the runtime error, alerting the developer to address this gap.
|
|
108
|
+
|
|
109
|
+
Note: for these examples, we're using an if/elsif/else chain, but the same concepts apply for `case/when/else` statements:
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
case letter
|
|
113
|
+
when 'A'
|
|
114
|
+
// handle a
|
|
115
|
+
when 'B'
|
|
116
|
+
// handle b
|
|
117
|
+
when 'C'
|
|
118
|
+
// handle c
|
|
119
|
+
else
|
|
120
|
+
raise "Unknown letter #{letter}"
|
|
121
|
+
end
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
This new syntax is better, but leaves some gaps:
|
|
125
|
+
|
|
126
|
+
1. Engineers taught to use the class `if/else` structure constantly introduce these problems.
|
|
127
|
+
2. The final raise statement is often "impossible" to access via testing, it's nature preventing access without mocking (especially when these types of clauses are part of private functions).
|
|
128
|
+
|
|
129
|
+
## The solution
|
|
130
|
+
|
|
131
|
+
But what if had a new type of case statement that could both ensure that all cases are correctly implemented and provide feedback if a new case has been introduced but not implemented?
|
|
132
|
+
|
|
133
|
+
Enter exhaustive case:
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
exhaustive_case letter do
|
|
137
|
+
on('A') { // handle A }
|
|
138
|
+
on('B') { // handle B }
|
|
139
|
+
on('C') { // handle C }
|
|
140
|
+
end
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
with the new syntax, `exhaustive_case` handles the final else statement and raises and error if there's an unexpected.
|
|
144
|
+
|
|
145
|
+
`exhaustive_case` also tries to provide a syntax simple to the native Ruby `case` switch, with multiple matchers supported.
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
```ruby
|
|
149
|
+
exhaustive_case letter do
|
|
150
|
+
on('A') { // handle A }
|
|
151
|
+
on('B', 'C') { // handle B or C }
|
|
152
|
+
end
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Enhanced validation with `of:` parameter
|
|
156
|
+
|
|
157
|
+
In situations where the central system relies on a series of strategies or an enumerated list, it's often unclear where a growing codebase new logic should be added.
|
|
158
|
+
|
|
159
|
+
By declaring explicitly the known list of forks, the list of forks can be centralized and then the test suite can provide feedback when a new entry to the list is added or removed.
|
|
160
|
+
|
|
161
|
+
For even stronger guarantees, you can specify the complete list of acceptable inputs using the `of:` parameter. This ensures that:
|
|
162
|
+
|
|
163
|
+
1. All declared cases must belong to the specified list
|
|
164
|
+
2. All values in the `of:` list must be handled by at least one case
|
|
165
|
+
|
|
166
|
+
```ruby
|
|
167
|
+
# Define all possible values upfront
|
|
168
|
+
VALID_LETTERS = ['A', 'B', 'C'].freeze
|
|
169
|
+
|
|
170
|
+
exhaustive_case letter, of: VALID_LETTERS do
|
|
171
|
+
on('A') { // handle A }
|
|
172
|
+
on('B') { // handle B }
|
|
173
|
+
on('C') { // handle C }
|
|
174
|
+
end
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
This will raise an `InvalidCaseError` if:
|
|
178
|
+
- You try to handle a case not in the `of:` list: `on('D') { ... }`
|
|
179
|
+
- You forget to handle all cases: missing `on('C') { ... }`
|
|
180
|
+
|
|
181
|
+
The `of:` parameter is optional - without it, `exhaustive_case` works as before, only validating that the input value has a matching case.
|
|
182
|
+
|
|
183
|
+
### Examples with `of:`
|
|
184
|
+
|
|
185
|
+
```ruby
|
|
186
|
+
# Status handling with validation
|
|
187
|
+
STATUSES = [:pending, :success, :failure].freeze
|
|
188
|
+
|
|
189
|
+
result = exhaustive_case status, of: STATUSES do
|
|
190
|
+
on(:pending) { "Processing..." }
|
|
191
|
+
on(:success) { "Completed successfully" }
|
|
192
|
+
on(:failure) { "Failed with error" }
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Multiple values per case still work
|
|
196
|
+
USER_TYPES = [:admin, :moderator, :user, :guest].freeze
|
|
197
|
+
|
|
198
|
+
permissions = exhaustive_case user_type, of: USER_TYPES do
|
|
199
|
+
on(:admin) { [:read, :write, :delete, :manage] }
|
|
200
|
+
on(:moderator, :user) { [:read, :write] }
|
|
201
|
+
on(:guest) { [:read] }
|
|
202
|
+
end
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
if a new status is added to `STATUSES` a test suite should reveal that a case is missing.
|
|
206
|
+
|
|
207
|
+
## Error Handling
|
|
208
|
+
|
|
209
|
+
The following are the expected errors when using the gem:
|
|
210
|
+
|
|
211
|
+
- **`UnhandledCaseError`** - Raised when the input value doesn't match any `on` clause
|
|
212
|
+
- **`InvalidCaseError`** - Raised when using `of:` and an `on` clause contains a value not in the allowed list
|
|
213
|
+
- **`MissingCaseError`** - Raised when using `of:` and not all allowed values have corresponding `on` clauses
|
|
214
|
+
|
|
215
|
+
## Contributing
|
|
216
|
+
|
|
217
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/ajsharma/exclusive_case.
|
|
218
|
+
|
|
219
|
+
## License
|
|
220
|
+
|
|
221
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/exclusive_case/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "exclusive_case"
|
|
7
|
+
spec.version = ExclusiveCase::VERSION
|
|
8
|
+
spec.authors = ["Ajay Sharma"]
|
|
9
|
+
spec.email = ["aj@ajsharma.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Exhaustive case statements for Ruby to prevent unhandled cases"
|
|
12
|
+
spec.description = "A Ruby gem that provides exhaustive case statement functionality to prevent bugs " \
|
|
13
|
+
"from unhandled cases when new values are added to a system."
|
|
14
|
+
spec.homepage = "https://github.com/ajsharma/exclusive_case"
|
|
15
|
+
spec.license = "MIT"
|
|
16
|
+
spec.required_ruby_version = ">= 3.0.0"
|
|
17
|
+
|
|
18
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
19
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
20
|
+
spec.metadata["source_code_uri"] = "https://github.com/ajsharma/exclusive_case"
|
|
21
|
+
spec.metadata["changelog_uri"] = "https://github.com/ajsharma/exclusive_case/blob/main/CHANGELOG.md"
|
|
22
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
23
|
+
|
|
24
|
+
# Specify which files should be added to the gem when it is released.
|
|
25
|
+
spec.files = Dir.chdir(__dir__) do
|
|
26
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
27
|
+
(File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
spec.bindir = "exe"
|
|
31
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
32
|
+
spec.require_paths = ["lib"]
|
|
33
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ExclusiveCase
|
|
4
|
+
# CaseBuilder handles the construction and execution of exhaustive case statements.
|
|
5
|
+
# It validates cases against an optional 'of' list and ensures all required cases are handled.
|
|
6
|
+
#
|
|
7
|
+
# @api private
|
|
8
|
+
class CaseBuilder
|
|
9
|
+
def initialize(value, of)
|
|
10
|
+
@value = value
|
|
11
|
+
@of = of
|
|
12
|
+
@cases = []
|
|
13
|
+
@matched = false
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def on(*matchers, &block)
|
|
17
|
+
validate_matchers(matchers) if @of
|
|
18
|
+
@cases << { matchers: matchers, block: block }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def execute
|
|
22
|
+
validate_completeness if @of
|
|
23
|
+
|
|
24
|
+
@cases.each do |case_def|
|
|
25
|
+
if case_def[:matchers].any? { |matcher| matcher == @value }
|
|
26
|
+
@matched = true
|
|
27
|
+
return case_def[:block].call
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
raise UnhandledCaseError, "No case handled value: #{@value.inspect}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def validate_matchers(matchers)
|
|
37
|
+
invalid_matchers = matchers.reject { |matcher| @of.include?(matcher) }
|
|
38
|
+
return if invalid_matchers.empty?
|
|
39
|
+
|
|
40
|
+
raise InvalidCaseError, "Invalid case(s): #{invalid_matchers.map(&:inspect).join(", ")}. " \
|
|
41
|
+
"Must be one of: #{@of.map(&:inspect).join(", ")}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def validate_completeness
|
|
45
|
+
declared_cases = @cases.flat_map { |case_def| case_def[:matchers] }.uniq
|
|
46
|
+
missing_cases = @of - declared_cases
|
|
47
|
+
return if missing_cases.empty?
|
|
48
|
+
|
|
49
|
+
raise MissingCaseError, "Missing case(s): #{missing_cases.map(&:inspect).join(", ")}. " \
|
|
50
|
+
"All values from 'of' must be handled: #{@of.map(&:inspect).join(", ")}"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "exclusive_case/version"
|
|
4
|
+
require_relative "exclusive_case/case_builder"
|
|
5
|
+
|
|
6
|
+
# ExclusiveCase provides exhaustive case statement functionality to prevent bugs
|
|
7
|
+
# from unhandled cases when new values are added to a system.
|
|
8
|
+
#
|
|
9
|
+
# @example Basic usage
|
|
10
|
+
# exhaustive_case letter do
|
|
11
|
+
# on('A') { "handle A" }
|
|
12
|
+
# on('B') { "handle B" }
|
|
13
|
+
# on('C') { "handle C" }
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# @example With validation using 'of' parameter
|
|
17
|
+
# exhaustive_case letter, of: ['A', 'B', 'C'] do
|
|
18
|
+
# on('A') { "handle A" }
|
|
19
|
+
# on('B') { "handle B" }
|
|
20
|
+
# on('C') { "handle C" }
|
|
21
|
+
# end
|
|
22
|
+
module ExclusiveCase
|
|
23
|
+
# Generic Error class for the gem
|
|
24
|
+
class Error < StandardError; end
|
|
25
|
+
|
|
26
|
+
# Indicates that an unknown input was passed into the system.
|
|
27
|
+
#
|
|
28
|
+
# Correct this by changing the input or know allowed values
|
|
29
|
+
class UnhandledCaseError < Error; end
|
|
30
|
+
|
|
31
|
+
# Indicates that a case was declared that is not in the allowed 'of' list
|
|
32
|
+
class InvalidCaseError < Error; end
|
|
33
|
+
|
|
34
|
+
# Indicates that one or more cases from the initial value list was not
|
|
35
|
+
# implemented
|
|
36
|
+
class MissingCaseError < Error; end
|
|
37
|
+
|
|
38
|
+
def exhaustive_case(value, of: nil, &block)
|
|
39
|
+
builder = CaseBuilder.new(value, of)
|
|
40
|
+
builder.instance_eval(&block)
|
|
41
|
+
builder.execute
|
|
42
|
+
end
|
|
43
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: exclusive_case
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Ajay Sharma
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: A Ruby gem that provides exhaustive case statement functionality to prevent
|
|
13
|
+
bugs from unhandled cases when new values are added to a system.
|
|
14
|
+
email:
|
|
15
|
+
- aj@ajsharma.com
|
|
16
|
+
executables: []
|
|
17
|
+
extensions: []
|
|
18
|
+
extra_rdoc_files: []
|
|
19
|
+
files:
|
|
20
|
+
- ".rspec"
|
|
21
|
+
- ".rubocop.yml"
|
|
22
|
+
- ".ruby-version"
|
|
23
|
+
- CHANGELOG.md
|
|
24
|
+
- LICENSE
|
|
25
|
+
- README.md
|
|
26
|
+
- Rakefile
|
|
27
|
+
- exclusive_case.gemspec
|
|
28
|
+
- lib/exclusive_case.rb
|
|
29
|
+
- lib/exclusive_case/case_builder.rb
|
|
30
|
+
- lib/exclusive_case/version.rb
|
|
31
|
+
homepage: https://github.com/ajsharma/exclusive_case
|
|
32
|
+
licenses:
|
|
33
|
+
- MIT
|
|
34
|
+
metadata:
|
|
35
|
+
allowed_push_host: https://rubygems.org
|
|
36
|
+
homepage_uri: https://github.com/ajsharma/exclusive_case
|
|
37
|
+
source_code_uri: https://github.com/ajsharma/exclusive_case
|
|
38
|
+
changelog_uri: https://github.com/ajsharma/exclusive_case/blob/main/CHANGELOG.md
|
|
39
|
+
rubygems_mfa_required: 'true'
|
|
40
|
+
rdoc_options: []
|
|
41
|
+
require_paths:
|
|
42
|
+
- lib
|
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: 3.0.0
|
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '0'
|
|
53
|
+
requirements: []
|
|
54
|
+
rubygems_version: 3.6.9
|
|
55
|
+
specification_version: 4
|
|
56
|
+
summary: Exhaustive case statements for Ruby to prevent unhandled cases
|
|
57
|
+
test_files: []
|