enumancer 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/lib/enum.rb +222 -0
- metadata +45 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 84c1bea1a7dd401a30ccc5688a86cfba9829a7cfceb58c5bb60b4124bbbacea2
|
|
4
|
+
data.tar.gz: ec380b6ec4a8d8b219995676508d02572b78ab40eb0080838cb14fded29a7048
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 7505cd08d8b118be3dac15c7e29b9b2b1372e93858b1889972e8112d0f6bb33e1246d874cd4423c2fab210f3c71e736455169f57deaa1e2fa391fe7418cc5fff
|
|
7
|
+
data.tar.gz: 5c290f4da876dd4edab3ded6760aa4ee1ccbe7243dbd13fdcae161872b9d72eb49381e8289f229abcaa78978050be0679de1639738fd0fd796f372b050299887
|
data/lib/enum.rb
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Enumancer::Enum provides a declarative, type-safe registry for named values.
|
|
4
|
+
# Each entry is unique by both name and value. Values can be of any type,
|
|
5
|
+
# but you may optionally declare a type constraint using `type`.
|
|
6
|
+
# If type is defined, values are strictly checked.
|
|
7
|
+
#
|
|
8
|
+
# Enum entries are registered via `.entry(name, value)` and accessed via `.name` or `[]`.
|
|
9
|
+
# Each entry becomes a singleton method of the class and returns an instance of the enum.
|
|
10
|
+
#
|
|
11
|
+
# You may enable strict mode via `type Klass, strict: true`, which disables fallbacks
|
|
12
|
+
# and raises errors when accessing unknown names or values.
|
|
13
|
+
#
|
|
14
|
+
# @example Define an enum
|
|
15
|
+
# class MyEnum < Enumancer::Enum
|
|
16
|
+
# type Integer, strict: true
|
|
17
|
+
# entry :low, 1
|
|
18
|
+
# entry :high, 2
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# @example Access values
|
|
22
|
+
# MyEnum[:low].value # => 1
|
|
23
|
+
# MyEnum.high.to_sym # => :low
|
|
24
|
+
# MyEnum.low == MyEnum[:low] # => true
|
|
25
|
+
#
|
|
26
|
+
# @example Serialize to JSON
|
|
27
|
+
# MyEnum.low.to_json # => '{"name":"low","value":1}'
|
|
28
|
+
#
|
|
29
|
+
# @example Deserialize from JSON
|
|
30
|
+
# MyEnum.from_json('{"name":"low"}') # => MyEnum.low
|
|
31
|
+
|
|
32
|
+
require 'json'
|
|
33
|
+
|
|
34
|
+
module Enumancer
|
|
35
|
+
class Enum
|
|
36
|
+
attr_reader :value
|
|
37
|
+
|
|
38
|
+
def initialize(value)
|
|
39
|
+
@value = value
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Returns the symbolic name of the value as a string
|
|
43
|
+
#
|
|
44
|
+
# @return [String]
|
|
45
|
+
def to_s
|
|
46
|
+
to_sym.to_s
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Returns the symbolic name of the value as a symbol
|
|
50
|
+
#
|
|
51
|
+
# @return [Symbol]
|
|
52
|
+
def to_sym
|
|
53
|
+
name = self.class.name_for(value)
|
|
54
|
+
raise KeyError, "Unregistered enum value: #{value.inspect}" if name.nil? && self.class.strict?
|
|
55
|
+
name || :unknown
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Returns a debug-friendly string representation
|
|
59
|
+
#
|
|
60
|
+
# @return [String]
|
|
61
|
+
def inspect
|
|
62
|
+
"#<#{self.class.name} #{to_sym.inspect}:#{value.inspect}>"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Equality based on class and value
|
|
66
|
+
#
|
|
67
|
+
# @param other [Object]
|
|
68
|
+
# @return [Boolean]
|
|
69
|
+
def ==(other)
|
|
70
|
+
other.is_a?(self.class) && other.value == value
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
alias eql? ==
|
|
74
|
+
def hash = value.hash
|
|
75
|
+
|
|
76
|
+
# Serializes the enum to JSON
|
|
77
|
+
#
|
|
78
|
+
# @return [String]
|
|
79
|
+
def to_json(*args)
|
|
80
|
+
{ name: to_sym, value: value }.to_json(*args)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
class << self
|
|
84
|
+
# Called when subclassing Enum
|
|
85
|
+
#
|
|
86
|
+
# @param subclass [Class]
|
|
87
|
+
# @return [void]
|
|
88
|
+
def inherited(subclass)
|
|
89
|
+
subclass.instance_variable_set(:@registry, {})
|
|
90
|
+
subclass.instance_variable_set(:@values, {})
|
|
91
|
+
subclass.instance_variable_set(:@strict_mode, false)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Declares the expected type of all enum values
|
|
95
|
+
#
|
|
96
|
+
# @param klass [Class] the type constraint for values
|
|
97
|
+
# @param strict [Boolean] whether to enable strict mode
|
|
98
|
+
# @return [void]
|
|
99
|
+
def type(klass, strict: false)
|
|
100
|
+
unless klass.is_a?(Class)
|
|
101
|
+
raise ArgumentError, "Expected a Class, got #{klass.inspect}"
|
|
102
|
+
end
|
|
103
|
+
@value_type = klass
|
|
104
|
+
@strict_mode = strict
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Returns whether strict mode is enabled
|
|
108
|
+
#
|
|
109
|
+
# @return [Boolean]
|
|
110
|
+
def strict?
|
|
111
|
+
@strict_mode == true
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Registers a new enum entry with a unique name and value
|
|
115
|
+
#
|
|
116
|
+
# @param name [Symbol, String] symbolic name of the entry
|
|
117
|
+
# @param value [Object] value of the entry
|
|
118
|
+
# @raise [ArgumentError] if name or value is already registered
|
|
119
|
+
# @raise [TypeError] if value does not match declared type
|
|
120
|
+
# @return [void]
|
|
121
|
+
def entry(name, value)
|
|
122
|
+
name = name.to_sym
|
|
123
|
+
|
|
124
|
+
if defined?(@value_type) && !value.is_a?(@value_type)
|
|
125
|
+
raise TypeError, "Invalid value type for #{name}: expected #{@value_type}, got #{value.class}"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
if @values.key?(value)
|
|
129
|
+
existing = @values[value]
|
|
130
|
+
raise ArgumentError, "Duplicate value #{value.inspect} for #{name}; already assigned to #{existing}"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
if @registry.key?(name)
|
|
134
|
+
raise ArgumentError, "Duplicate name #{name}; already registered with value #{@registry[name].value.inspect}"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
instance = new(value)
|
|
138
|
+
@registry[name] = instance
|
|
139
|
+
@values[value] = name
|
|
140
|
+
|
|
141
|
+
define_singleton_method(name) { instance }
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Retrieves an enum instance by name
|
|
145
|
+
#
|
|
146
|
+
# @param name [Symbol, String]
|
|
147
|
+
# @return [Enumancer::Enum, nil]
|
|
148
|
+
def [](name)
|
|
149
|
+
@registry[name.to_sym]
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Retrieves an enum instance by name, raising if not found
|
|
153
|
+
#
|
|
154
|
+
# @param name [Symbol, String]
|
|
155
|
+
# @return [Enumancer::Enum]
|
|
156
|
+
# @raise [KeyError]
|
|
157
|
+
def fetch(name)
|
|
158
|
+
@registry.fetch(name.to_sym)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Resolves the symbolic name for a given value
|
|
162
|
+
#
|
|
163
|
+
# @param value [Object]
|
|
164
|
+
# @return [Symbol, nil]
|
|
165
|
+
def name_for(value)
|
|
166
|
+
@values[value]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Returns all registered enum instances
|
|
170
|
+
#
|
|
171
|
+
# @return [Array<Enumancer::Enum>]
|
|
172
|
+
def all
|
|
173
|
+
@registry.values
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Returns all registered names
|
|
177
|
+
#
|
|
178
|
+
# @return [Array<Symbol>]
|
|
179
|
+
def keys
|
|
180
|
+
@registry.keys
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Returns all raw values
|
|
184
|
+
#
|
|
185
|
+
# @return [Array<Object>]
|
|
186
|
+
def values
|
|
187
|
+
@registry.values.map(&:value)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Removes an enum entry by name
|
|
191
|
+
#
|
|
192
|
+
# @param name [Symbol, String]
|
|
193
|
+
# @return [Enumancer::Enum, nil] the removed entry or nil
|
|
194
|
+
def remove(name)
|
|
195
|
+
name = name.to_sym
|
|
196
|
+
entry = @registry.delete(name)
|
|
197
|
+
if entry
|
|
198
|
+
@values.delete(entry.value)
|
|
199
|
+
singleton_class.undef_method(name) if respond_to?(name)
|
|
200
|
+
end
|
|
201
|
+
entry
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Deserializes an enum from JSON
|
|
205
|
+
#
|
|
206
|
+
# @param json [String]
|
|
207
|
+
# @return [Enumancer::Enum, nil]
|
|
208
|
+
# @raise [KeyError] if strict and name is unknown
|
|
209
|
+
def from_json(json)
|
|
210
|
+
data = JSON.parse(json, symbolize_names: true)
|
|
211
|
+
name = data[:name]
|
|
212
|
+
raise KeyError, "Missing 'name' key in JSON" if name.nil?
|
|
213
|
+
|
|
214
|
+
entry = self[name]
|
|
215
|
+
if strict? && entry.nil?
|
|
216
|
+
raise KeyError, "Unregistered enum name: #{name.inspect}"
|
|
217
|
+
end
|
|
218
|
+
entry
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: enumancer
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Łasačka
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: |2
|
|
13
|
+
Enumancer provides a predictable, type-safe registry for named values.
|
|
14
|
+
Each entry is unique by both name and value, with optional type constraints and strict mode.
|
|
15
|
+
email: saikinmirai@gmail.com
|
|
16
|
+
executables: []
|
|
17
|
+
extensions: []
|
|
18
|
+
extra_rdoc_files: []
|
|
19
|
+
files:
|
|
20
|
+
- lib/enum.rb
|
|
21
|
+
homepage: https://github.com/lasaczka/enumancer
|
|
22
|
+
licenses:
|
|
23
|
+
- BSD-3-Clause-Attribution
|
|
24
|
+
metadata:
|
|
25
|
+
changelog_uri: https://github.com/lasaczka/enumancer/blob/main/CHANGELOG.md
|
|
26
|
+
bug_tracker_uri: https://github.com/lasaczka/enumancer/issues
|
|
27
|
+
documentation_uri: https://rubydoc.info/gems/kolor/1.0.0
|
|
28
|
+
rdoc_options: []
|
|
29
|
+
require_paths:
|
|
30
|
+
- lib
|
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
32
|
+
requirements:
|
|
33
|
+
- - ">="
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: 3.0.0
|
|
36
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
requirements: []
|
|
42
|
+
rubygems_version: 3.8.0.dev
|
|
43
|
+
specification_version: 4
|
|
44
|
+
summary: Easy to use typed enums.
|
|
45
|
+
test_files: []
|