typesafe_enum 0.2.2 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/build.yml +30 -0
- data/.gitignore +209 -29
- data/.idea/.rakeTasks +7 -0
- data/.idea/codeStyles/Project.xml +45 -0
- data/.idea/codeStyles/codeStyleConfig.xml +5 -0
- data/.idea/go.imports.xml +6 -0
- data/.idea/inspectionProfiles/Project_Default.xml +18 -0
- data/.idea/misc.xml +9 -0
- data/.idea/modules.xml +8 -0
- data/.idea/typesafe_enum.iml +78 -0
- data/.idea/vcs.xml +6 -0
- data/.rubocop.yml +184 -22
- data/.ruby-version +1 -1
- data/.travis.yml +7 -0
- data/CHANGES.md +17 -0
- data/README.md +15 -1
- data/Rakefile +8 -42
- data/lib/typesafe_enum/base.rb +18 -98
- data/lib/typesafe_enum/class_methods.rb +167 -0
- data/lib/typesafe_enum/exceptions.rb +7 -0
- data/lib/typesafe_enum/module_info.rb +8 -6
- data/rakelib/coverage.rake +11 -0
- data/rakelib/gem.rake +54 -0
- data/rakelib/rubocop.rake +18 -0
- data/rakelib/spec.rake +2 -0
- data/spec/.rubocop.yml +3 -0
- data/spec/unit/typesafe_enum/base_spec.rb +199 -7
- data/typesafe_enum.gemspec +9 -5
- metadata +57 -19
data/.rubocop.yml
CHANGED
@@ -1,8 +1,3 @@
|
|
1
|
-
# Disable compact style check for example.rb
|
2
|
-
Style/ClassAndModuleChildren:
|
3
|
-
Exclude:
|
4
|
-
- 'example.rb'
|
5
|
-
|
6
1
|
# Allow one line around block body (Layout/EmptyLines will still disallow two or more)
|
7
2
|
Layout/EmptyLinesAroundBlockBody:
|
8
3
|
Enabled: false
|
@@ -27,11 +22,6 @@ Layout/MultilineMethodCallIndentation:
|
|
27
22
|
Layout/MultilineOperationIndentation:
|
28
23
|
Enabled: false
|
29
24
|
|
30
|
-
# Just because something looks like an accessor doesn't mean it is one
|
31
|
-
Naming/PredicateName:
|
32
|
-
Exclude:
|
33
|
-
- 'app/controllers/application_controller.rb'
|
34
|
-
|
35
25
|
# Confusing and weird
|
36
26
|
Naming/VariableNumber:
|
37
27
|
Enabled: False
|
@@ -60,18 +50,6 @@ Style/CommentedKeyword:
|
|
60
50
|
Style/Documentation:
|
61
51
|
Enabled: false
|
62
52
|
|
63
|
-
# Added in RuboCop 0.80
|
64
|
-
Style/HashEachMethods:
|
65
|
-
Enabled: true
|
66
|
-
|
67
|
-
# Added in RuboCop 0.80
|
68
|
-
Style/HashTransformKeys:
|
69
|
-
Enabled: true
|
70
|
-
|
71
|
-
# Added in RuboCop 0.80
|
72
|
-
Style/HashTransformValues:
|
73
|
-
Enabled: true
|
74
|
-
|
75
53
|
# Adding more line noise to format strings will not improve them
|
76
54
|
Style/FormatStringToken:
|
77
55
|
Enabled: false
|
@@ -95,3 +73,187 @@ Style/Lambda:
|
|
95
73
|
# Unclear why it's a good idea to give parameters semantically meaningless names
|
96
74
|
Style/SingleLineBlockParams:
|
97
75
|
Enabled: false
|
76
|
+
|
77
|
+
############################################################
|
78
|
+
# Added in RuboCop 0.80
|
79
|
+
|
80
|
+
Style/HashEachMethods:
|
81
|
+
Enabled: true
|
82
|
+
|
83
|
+
Style/HashTransformKeys:
|
84
|
+
Enabled: true
|
85
|
+
|
86
|
+
Style/HashTransformValues:
|
87
|
+
Enabled: true
|
88
|
+
|
89
|
+
############################################################
|
90
|
+
# Added in RuboCop 0.81
|
91
|
+
|
92
|
+
Lint/StructNewOverride:
|
93
|
+
Enabled: true
|
94
|
+
|
95
|
+
Lint/RaiseException:
|
96
|
+
Enabled: true
|
97
|
+
|
98
|
+
############################################################
|
99
|
+
# Added in RuboCop 0.82
|
100
|
+
|
101
|
+
Layout/SpaceAroundMethodCallOperator:
|
102
|
+
Enabled: true
|
103
|
+
|
104
|
+
Style/ExponentialNotation:
|
105
|
+
Enabled: false
|
106
|
+
|
107
|
+
############################################################
|
108
|
+
# Added in RuboCop 0.83
|
109
|
+
|
110
|
+
Layout/EmptyLinesAroundAttributeAccessor:
|
111
|
+
Enabled: true
|
112
|
+
|
113
|
+
Style/SlicingWithRange:
|
114
|
+
Enabled: true
|
115
|
+
|
116
|
+
############################################################
|
117
|
+
# Added in RuboCop 0.84
|
118
|
+
|
119
|
+
Lint/DeprecatedOpenSSLConstant:
|
120
|
+
Enabled: true
|
121
|
+
|
122
|
+
############################################################
|
123
|
+
# Added in RuboCop 0.85
|
124
|
+
|
125
|
+
Lint/MixedRegexpCaptureTypes:
|
126
|
+
Enabled: true
|
127
|
+
|
128
|
+
Style/RedundantRegexpEscape:
|
129
|
+
Enabled: true
|
130
|
+
|
131
|
+
Style/RedundantRegexpCharacterClass:
|
132
|
+
Enabled: true
|
133
|
+
|
134
|
+
############################################################
|
135
|
+
# Added in Rubocop 0.86
|
136
|
+
|
137
|
+
Style/RedundantFetchBlock:
|
138
|
+
Enabled: true
|
139
|
+
|
140
|
+
############################################################
|
141
|
+
# Added in Rubocop 0.87
|
142
|
+
|
143
|
+
# Sometimes we separate things for a reason
|
144
|
+
Style/AccessorGrouping:
|
145
|
+
Enabled: false
|
146
|
+
|
147
|
+
Style/BisectedAttrAccessor:
|
148
|
+
Enabled: true
|
149
|
+
|
150
|
+
Style/RedundantAssignment:
|
151
|
+
Enabled: true
|
152
|
+
|
153
|
+
############################################################
|
154
|
+
# Added in Rubocop 0.88
|
155
|
+
|
156
|
+
Lint/DuplicateElsifCondition:
|
157
|
+
Enabled: true
|
158
|
+
|
159
|
+
Style/ArrayCoercion:
|
160
|
+
Enabled: true
|
161
|
+
|
162
|
+
Style/CaseLikeIf:
|
163
|
+
Enabled: true
|
164
|
+
|
165
|
+
Style/HashAsLastArrayItem:
|
166
|
+
Enabled: true
|
167
|
+
|
168
|
+
Style/HashLikeCase:
|
169
|
+
Enabled: true
|
170
|
+
|
171
|
+
Style/RedundantFileExtensionInRequire:
|
172
|
+
Enabled: true
|
173
|
+
|
174
|
+
############################################################
|
175
|
+
# Added in Rubocop 0.89
|
176
|
+
|
177
|
+
Lint/BinaryOperatorWithIdenticalOperands:
|
178
|
+
Enabled: true
|
179
|
+
|
180
|
+
Lint/DuplicateRescueException:
|
181
|
+
Enabled: true
|
182
|
+
|
183
|
+
Lint/EmptyConditionalBody:
|
184
|
+
Enabled: true
|
185
|
+
|
186
|
+
Lint/FloatComparison:
|
187
|
+
Enabled: true
|
188
|
+
|
189
|
+
Lint/MissingSuper:
|
190
|
+
Enabled: true
|
191
|
+
|
192
|
+
Lint/OutOfRangeRegexpRef:
|
193
|
+
Enabled: true
|
194
|
+
|
195
|
+
Lint/SelfAssignment:
|
196
|
+
Enabled: true
|
197
|
+
|
198
|
+
Lint/TopLevelReturnWithArgument:
|
199
|
+
Enabled: true
|
200
|
+
|
201
|
+
Lint/UnreachableLoop:
|
202
|
+
Enabled: true
|
203
|
+
|
204
|
+
Style/ExplicitBlockArgument:
|
205
|
+
Enabled: true
|
206
|
+
|
207
|
+
Style/GlobalStdStream:
|
208
|
+
Enabled: true
|
209
|
+
|
210
|
+
Style/OptionalBooleanParameter:
|
211
|
+
Enabled: true
|
212
|
+
|
213
|
+
Style/SingleArgumentDig:
|
214
|
+
Enabled: true
|
215
|
+
|
216
|
+
Style/SoleNestedConditional:
|
217
|
+
Enabled: true
|
218
|
+
|
219
|
+
Style/StringConcatenation:
|
220
|
+
Enabled: true
|
221
|
+
|
222
|
+
############################################################
|
223
|
+
# Added in Rubocop 0.90
|
224
|
+
|
225
|
+
Lint/DuplicateRequire:
|
226
|
+
Enabled: true
|
227
|
+
|
228
|
+
Lint/EmptyFile:
|
229
|
+
Enabled: true
|
230
|
+
|
231
|
+
Lint/TrailingCommaInAttributeDeclaration:
|
232
|
+
Enabled: true
|
233
|
+
|
234
|
+
Lint/UselessMethodDefinition:
|
235
|
+
Enabled: true
|
236
|
+
|
237
|
+
Style/CombinableLoops:
|
238
|
+
Enabled: true
|
239
|
+
|
240
|
+
Style/KeywordParametersOrder:
|
241
|
+
Enabled: true
|
242
|
+
|
243
|
+
Style/RedundantSelfAssignment:
|
244
|
+
Enabled: true
|
245
|
+
|
246
|
+
############################################################
|
247
|
+
# Added in Rubocop 0.91
|
248
|
+
|
249
|
+
Layout/BeginEndAlignment:
|
250
|
+
Enabled: true
|
251
|
+
|
252
|
+
Lint/ConstantDefinitionInBlock:
|
253
|
+
Enabled: true
|
254
|
+
|
255
|
+
Lint/IdentityComparison:
|
256
|
+
Enabled: true
|
257
|
+
|
258
|
+
Lint/UselessTimes:
|
259
|
+
Enabled: true
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.7
|
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
## 0.3.1 (next)
|
2
|
+
|
3
|
+
- Add `values`, `keys`, `each_value`, `each_key`, `find_by_value!`, `find_by_value_str!`
|
4
|
+
(PR [#8](https://github.com/dmolesUC/typesafe_enum/pull/8))
|
5
|
+
- Set minimum required_ruby_version to 2.7 to reflect 2.6
|
6
|
+
[end of life](https://www.ruby-lang.org/en/news/2022/04/12/ruby-2-6-10-released/)
|
7
|
+
- Bump .ruby-version to 2.7
|
8
|
+
- Bump Rake version to 13 for 2.x/3.x compatibility
|
9
|
+
|
10
|
+
## 0.3.0 (26 October 2020)
|
11
|
+
|
12
|
+
- Support explicit nil values
|
13
|
+
- Update author email in gemspec
|
14
|
+
- Update RuboCop to version 0.91 and pin version
|
15
|
+
- Set minimum required_ruby_version to 2.6.0
|
16
|
+
- Bump .ruby-version to 2.6.6
|
17
|
+
|
1
18
|
## 0.2.2 (23 April 2020)
|
2
19
|
|
3
20
|
- Implement [`Enumerable`](https://ruby-doc.org/core-2.6.5/Enumerable.html)
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# TypesafeEnum
|
2
2
|
|
3
|
-
[![Build Status](https://
|
3
|
+
[![Build Status](https://github.com/dmolesUC/typesafe_enum/actions/workflows/build.yml/badge.svg?branch=master)](https://travis-ci.org/dmolesUC/typesafe_enum)
|
4
4
|
[![Code Climate](https://codeclimate.com/github/dmolesUC/typesafe_enum.svg)](https://codeclimate.com/github/dmolesUC/typesafe_enum)
|
5
5
|
[![Inline docs](http://inch-ci.org/github/dmolesUC/typesafe_enum.svg)](http://inch-ci.org/github/dmolesUC/typesafe_enum)
|
6
6
|
[![Gem Version](https://img.shields.io/gem/v/typesafe_enum.svg)](https://github.com/dmolesUC/typesafe_enum/releases)
|
@@ -93,6 +93,20 @@ Scale::KILO.value
|
|
93
93
|
# => 1000
|
94
94
|
```
|
95
95
|
|
96
|
+
Even `nil` is a valid value (if set explicitly):
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
class Scheme < TypesafeEnum::Base
|
100
|
+
new :HTTP, 'http'
|
101
|
+
new :HTTPS, 'https'
|
102
|
+
new :EXAMPLE, 'example'
|
103
|
+
new :UNKNOWN, nil
|
104
|
+
end
|
105
|
+
|
106
|
+
Scheme::UNKNOWN.value
|
107
|
+
# => nil
|
108
|
+
```
|
109
|
+
|
96
110
|
Declaring two instances with the same key will produce an error:
|
97
111
|
|
98
112
|
```ruby
|
data/Rakefile
CHANGED
@@ -1,51 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
#
|
5
|
-
|
6
|
-
require 'rspec/core'
|
7
|
-
require 'rspec/core/rake_task'
|
8
|
-
|
9
|
-
namespace :spec do
|
10
|
-
|
11
|
-
desc 'Run all unit tests'
|
12
|
-
RSpec::Core::RakeTask.new(:unit) do |task|
|
13
|
-
task.rspec_opts = %w[--color --format documentation --order default]
|
14
|
-
task.pattern = 'unit/**/*_spec.rb'
|
15
|
-
end
|
16
|
-
|
17
|
-
task all: [:unit]
|
18
|
-
end
|
19
|
-
|
20
|
-
desc 'Run all tests'
|
21
|
-
task spec: 'spec:all'
|
22
|
-
|
23
|
-
# ------------------------------------------------------------
|
24
|
-
# Coverage
|
25
|
-
|
26
|
-
desc 'Run all unit tests with coverage'
|
27
|
-
task :coverage do
|
28
|
-
ENV['COVERAGE'] = 'true'
|
29
|
-
Rake::Task['spec:unit'].execute
|
30
|
-
end
|
31
|
-
|
32
|
-
# ------------------------------------------------------------
|
33
|
-
# RuboCop
|
34
|
-
|
35
|
-
require 'rubocop/rake_task'
|
36
|
-
RuboCop::RakeTask.new
|
3
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('Gemfile', __dir__)
|
4
|
+
require 'bundler/setup' # Set up gems listed in the Gemfile.
|
37
5
|
|
38
6
|
# ------------------------------------------------------------
|
39
|
-
#
|
7
|
+
# Application code
|
40
8
|
|
41
|
-
|
42
|
-
|
43
|
-
task.rspec_opts = %w[--color --format documentation --order default]
|
44
|
-
task.pattern = 'todo.rb'
|
9
|
+
File.expand_path('lib', __dir__).tap do |lib|
|
10
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
45
11
|
end
|
46
12
|
|
47
13
|
# ------------------------------------------------------------
|
48
|
-
#
|
14
|
+
# Custom tasks
|
49
15
|
|
50
|
-
desc 'Run
|
51
|
-
task default: %i[coverage rubocop]
|
16
|
+
desc 'Run tests, check test coverage, check code style, build gem'
|
17
|
+
task default: %i[coverage rubocop gem]
|
data/lib/typesafe_enum/base.rb
CHANGED
@@ -1,108 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'typesafe_enum/exceptions'
|
4
|
+
require 'typesafe_enum/class_methods'
|
5
|
+
|
3
6
|
# A Ruby implementation of Joshua Bloch's
|
4
7
|
# [typesafe enum pattern](http://www.oracle.com/technetwork/java/page1-139488.html#replaceenums)
|
5
8
|
module TypesafeEnum
|
9
|
+
|
6
10
|
# Base class for typesafe enum classes.
|
7
11
|
class Base
|
8
12
|
include Comparable
|
9
13
|
|
10
14
|
class << self
|
11
|
-
include
|
12
|
-
|
13
|
-
# Returns an array of the enum instances in declaration order
|
14
|
-
# @return [Array<self>] All instances of this enum, in declaration order
|
15
|
-
def to_a
|
16
|
-
as_array.dup
|
17
|
-
end
|
18
|
-
|
19
|
-
# Returns the number of enum instances
|
20
|
-
# @return [Integer] the number of instances
|
21
|
-
def size
|
22
|
-
as_array.size
|
23
|
-
end
|
24
|
-
|
25
|
-
# Iterates over the set of enum instances
|
26
|
-
# @yield [self] Each instance of this enum, in declaration order
|
27
|
-
# @return [Enumerator<self>] All instances of this enum, in declaration order
|
28
|
-
def each(&block)
|
29
|
-
to_a.each(&block)
|
30
|
-
end
|
31
|
-
|
32
|
-
# Looks up an enum instance based on its key
|
33
|
-
# @param key [Symbol] the key to look up
|
34
|
-
# @return [self, nil] the corresponding enum instance, or nil
|
35
|
-
def find_by_key(key)
|
36
|
-
by_key[key]
|
37
|
-
end
|
38
|
-
|
39
|
-
# Looks up an enum instance based on its value
|
40
|
-
# @param value [Object] the value to look up
|
41
|
-
# @return [self, nil] the corresponding enum instance, or nil
|
42
|
-
def find_by_value(value)
|
43
|
-
by_value[value]
|
44
|
-
end
|
45
|
-
|
46
|
-
# Looks up an enum instance based on the string representation of its value
|
47
|
-
# @param value_str [String] the string form of the value
|
48
|
-
# @return [self, nil] the corresponding enum instance, or nil
|
49
|
-
def find_by_value_str(value_str)
|
50
|
-
value_str = value_str.to_s
|
51
|
-
by_value_str[value_str]
|
52
|
-
end
|
53
|
-
|
54
|
-
# Looks up an enum instance based on its ordinal
|
55
|
-
# @param ord [Integer] the ordinal to look up
|
56
|
-
# @return [self, nil] the corresponding enum instance, or nil
|
57
|
-
def find_by_ord(ord)
|
58
|
-
return nil if ord > size || ord.negative?
|
59
|
-
|
60
|
-
as_array[ord]
|
61
|
-
end
|
62
|
-
|
63
|
-
private
|
64
|
-
|
65
|
-
def by_key
|
66
|
-
@by_key ||= {}
|
67
|
-
end
|
68
|
-
|
69
|
-
def by_value
|
70
|
-
@by_value ||= {}
|
71
|
-
end
|
72
|
-
|
73
|
-
def by_value_str
|
74
|
-
@by_value_str ||= {}
|
75
|
-
end
|
76
|
-
|
77
|
-
def as_array
|
78
|
-
@as_array ||= []
|
79
|
-
end
|
80
|
-
|
81
|
-
def valid_key_and_value(instance)
|
82
|
-
key = instance.key
|
83
|
-
value = instance.value
|
84
|
-
if (found = find_by_key(key))
|
85
|
-
raise NameError, "#{name}::#{key} already exists" unless value == found.value
|
86
|
-
|
87
|
-
warn("ignoring redeclaration of #{name}::#{key} with value #{value} (source: #{caller(5..5).first})")
|
88
|
-
nil
|
89
|
-
else
|
90
|
-
raise NameError, "A #{name} instance with value '#{value}' already exists" if find_by_value(value)
|
91
|
-
|
92
|
-
[key, value]
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
def register(instance)
|
97
|
-
key, value = valid_key_and_value(instance)
|
98
|
-
return unless key && value
|
99
|
-
|
100
|
-
const_set(key.to_s, instance)
|
101
|
-
by_key[key] = instance
|
102
|
-
by_value[value] = instance
|
103
|
-
by_value_str[value.to_s] = instance
|
104
|
-
as_array << instance
|
105
|
-
end
|
15
|
+
include ClassMethods
|
106
16
|
end
|
107
17
|
|
108
18
|
# The symbol key for the enum instance
|
@@ -123,6 +33,9 @@ module TypesafeEnum
|
|
123
33
|
# the same enum instance; 1 if this value follows `other`; `nil` if `other`
|
124
34
|
# is not an instance of this enum class
|
125
35
|
def <=>(other)
|
36
|
+
# in the case where the enum being compared is actually the parent
|
37
|
+
# class, only `==` will work correctly & we cannot use #is_a? or
|
38
|
+
# #instance_of?
|
126
39
|
ord <=> other.ord if self.class == other.class
|
127
40
|
end
|
128
41
|
|
@@ -137,17 +50,24 @@ module TypesafeEnum
|
|
137
50
|
end
|
138
51
|
end
|
139
52
|
|
53
|
+
# Default implementation includes the enum class, `key`,
|
54
|
+
# `ord` and `value`.
|
55
|
+
# @return [String] a string representation of the enum instance
|
140
56
|
def to_s
|
141
|
-
"#{self.class}::#{key} [#{ord}] -> #{value}"
|
57
|
+
"#{self.class}::#{key} [#{ord}] -> #{value.inspect}"
|
142
58
|
end
|
143
59
|
|
144
60
|
private
|
145
61
|
|
146
|
-
|
62
|
+
IMPLICIT = Class.new.new
|
63
|
+
private_constant :IMPLICIT
|
64
|
+
|
65
|
+
# TODO: is documentation on this still accurate? does it still need to be private?
|
66
|
+
def initialize(key, value = IMPLICIT, &block)
|
147
67
|
raise TypeError, "#{key} is not a symbol" unless key.is_a?(Symbol)
|
148
68
|
|
149
69
|
@key = key
|
150
|
-
@value = value
|
70
|
+
@value = value == IMPLICIT ? key.to_s.downcase : value
|
151
71
|
@ord = self.class.size
|
152
72
|
self.class.class_exec(self) do |instance|
|
153
73
|
register(instance)
|
@@ -0,0 +1,167 @@
|
|
1
|
+
module TypesafeEnum
|
2
|
+
# Class methods for {{TypesafeEnum::Base}}.
|
3
|
+
module ClassMethods
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
# Returns an array of the enum instances in declaration order
|
7
|
+
# @return [Array<self>] All instances of this enum, in declaration order
|
8
|
+
def to_a
|
9
|
+
as_array.dup
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns the number of enum instances
|
13
|
+
# @return [Integer] the number of instances
|
14
|
+
def size
|
15
|
+
as_array.size
|
16
|
+
end
|
17
|
+
|
18
|
+
# Iterates over the set of enum instances
|
19
|
+
# @yield [self] Each instance of this enum, in declaration order
|
20
|
+
# @return [Enumerator<self>] All instances of this enum, in declaration order
|
21
|
+
def each(&block)
|
22
|
+
to_a.each(&block)
|
23
|
+
end
|
24
|
+
|
25
|
+
# The set of all keys of all enum instances
|
26
|
+
# @return [Enumerator<Symbol>] All keys of all enums, in declaration order
|
27
|
+
def keys
|
28
|
+
to_a.map(&:key)
|
29
|
+
end
|
30
|
+
|
31
|
+
# The set of all values of all enum instances
|
32
|
+
# @return [Enumerator<Object>] All values of all enums, in declaration order
|
33
|
+
def values
|
34
|
+
to_a.map(&:value)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Iterates over the set of keys of all instances (#keys)
|
38
|
+
# @yield [Enumerator<Symbol>] Each key of each instance of this enum, in declaration order
|
39
|
+
# @return [Enumerator<Symbol>]
|
40
|
+
def each_key(&block)
|
41
|
+
keys.each(&block)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Iterates over the set of values of all instances (#values)
|
45
|
+
# @yield [Enumerator<Object>] Each value of each instance of this enum, in declaration order
|
46
|
+
# @return [Enumerator<Object>]
|
47
|
+
def each_value(&block)
|
48
|
+
values.each(&block)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Looks up an enum instance based on its key
|
52
|
+
# @param key [Symbol] the key to look up
|
53
|
+
# @return [self, nil] the corresponding enum instance, or nil
|
54
|
+
def find_by_key(key)
|
55
|
+
by_key[key]
|
56
|
+
end
|
57
|
+
|
58
|
+
# Looks up an enum instance based on its value
|
59
|
+
# @param value [Object] the value to look up
|
60
|
+
# @return [self, nil] the corresponding enum instance, or nil
|
61
|
+
def find_by_value(value)
|
62
|
+
by_value[value]
|
63
|
+
end
|
64
|
+
|
65
|
+
# Looks up an enum instance based on its value
|
66
|
+
# @param value [Object] the value to look up
|
67
|
+
# @return [self, EnumValidationError] the corresponding enum instance, or throws #EnumValidationError
|
68
|
+
def find_by_value!(value)
|
69
|
+
valid = find_by_value(value)
|
70
|
+
return valid unless valid.nil?
|
71
|
+
|
72
|
+
raise Exceptions::EnumValidationError, "#{class_name}: #{value} is absurd"
|
73
|
+
end
|
74
|
+
|
75
|
+
# Looks up an enum instance based on the string representation of its value
|
76
|
+
# @param value_str [String] the string form of the value
|
77
|
+
# @return [self, nil] the corresponding enum instance, or nil
|
78
|
+
def find_by_value_str(value_str)
|
79
|
+
value_str = value_str.to_s
|
80
|
+
by_value_str[value_str]
|
81
|
+
end
|
82
|
+
|
83
|
+
# Looks up an enum instance based on the string representation of its value
|
84
|
+
# @param value_str [String] the string form of the value
|
85
|
+
# @return [self, EnumValidationError] the corresponding enum instance, or throws #EnumValidationError
|
86
|
+
def find_by_value_str!(value_str)
|
87
|
+
valid = find_by_value_str(value_str)
|
88
|
+
return valid unless valid.nil?
|
89
|
+
|
90
|
+
raise Exceptions::EnumValidationError, "#{class_name}: #{value_str} is absurd"
|
91
|
+
end
|
92
|
+
|
93
|
+
# Looks up an enum instance based on its ordinal
|
94
|
+
# @param ord [Integer] the ordinal to look up
|
95
|
+
# @return [self, nil] the corresponding enum instance, or nil
|
96
|
+
def find_by_ord(ord)
|
97
|
+
return nil if ord > size || ord.negative?
|
98
|
+
|
99
|
+
as_array[ord]
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def by_key
|
105
|
+
@by_key ||= {}
|
106
|
+
end
|
107
|
+
|
108
|
+
def by_value
|
109
|
+
@by_value ||= {}
|
110
|
+
end
|
111
|
+
|
112
|
+
def by_value_str
|
113
|
+
@by_value_str ||= {}
|
114
|
+
end
|
115
|
+
|
116
|
+
def as_array
|
117
|
+
@as_array ||= []
|
118
|
+
end
|
119
|
+
|
120
|
+
def valid_key_and_value(instance)
|
121
|
+
return unless (key = valid_key(instance))
|
122
|
+
|
123
|
+
[key, valid_value(instance)]
|
124
|
+
end
|
125
|
+
|
126
|
+
def valid_key(instance)
|
127
|
+
key = instance.key
|
128
|
+
return key unless (found = find_by_key(key))
|
129
|
+
|
130
|
+
value = instance.value
|
131
|
+
raise NameError, "#{name}::#{key} already exists with value #{found.value.inspect}" unless value == found.value
|
132
|
+
|
133
|
+
warn("ignoring redeclaration of #{name}::#{key} with value #{value.inspect} (source: #{caller(6..6).first})")
|
134
|
+
end
|
135
|
+
|
136
|
+
def valid_value(instance)
|
137
|
+
value = instance.value
|
138
|
+
return value unless (found = find_by_value(value))
|
139
|
+
|
140
|
+
key = instance.key
|
141
|
+
raise NameError, "A #{name} instance with value #{value.inspect} already exists: #{found.key}" unless key == found.key
|
142
|
+
|
143
|
+
# valid_key() should already have warned us, and valid_key_and_value() should have exited early, but just in case
|
144
|
+
# :nocov:
|
145
|
+
warn("ignoring redeclaration of #{name}::#{key} with value #{value.inspect} (source: #{caller(6..6).first})")
|
146
|
+
# :nocov:
|
147
|
+
end
|
148
|
+
|
149
|
+
def register(instance)
|
150
|
+
key, value = valid_key_and_value(instance)
|
151
|
+
return unless key
|
152
|
+
|
153
|
+
const_set(key.to_s, instance)
|
154
|
+
by_key[key] = instance
|
155
|
+
by_value[value] = instance
|
156
|
+
by_value_str[value.to_s] = instance
|
157
|
+
as_array << instance
|
158
|
+
end
|
159
|
+
|
160
|
+
# Returns the demodulized class name of the inheriting class
|
161
|
+
# @return [String] The demodulized class name
|
162
|
+
def class_name
|
163
|
+
name.split('::').last
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
end
|
@@ -1,12 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module TypesafeEnum
|
4
|
-
|
5
|
-
|
4
|
+
module ModuleInfo
|
5
|
+
# The name of this gem
|
6
|
+
NAME = 'typesafe_enum'
|
6
7
|
|
7
|
-
|
8
|
-
|
8
|
+
# The version of this gem
|
9
|
+
VERSION = '0.3.1'
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
# The copyright notice for this gem
|
12
|
+
COPYRIGHT = 'Copyright (c) 2022 The Regents of the University of California'
|
13
|
+
end
|
12
14
|
end
|