Class: IS::Enum
- Inherits:
-
Object
- Object
- IS::Enum
- Extended by:
- Enumerable
- Includes:
- Comparable
- Defined in:
- lib/is-enum.rb,
lib/is-enum/info.rb
Overview
Thread safety
Enum definition (Enum.define) and finalization (Enum.finalize!) are thread-safe. Lookup operations are thread-safe after definition.
Class variables
Uses class variables ( @@enums, @@mutex ) shared across inheritance hierarchy. All enum classes register in global @@enums for Enum.parse.
Custom attributes
Additional attributes passed to Enum.define are stored in ‘@attrs`. Subclasses may access this hash directly to implement custom properties.
class Status < IS::Enum
define :error, 1, http_code: 500, retryable: false
def http_code
@attrs[:http_code]
end
def retryable?
@attrs[:retryable]
end
end
Defined Under Namespace
Modules: Info
Instance Attribute Summary collapse
-
#description ⇒ String?
readonly
Optional value description.
-
#name ⇒ Symbol
readonly
Value name.
-
#order_no ⇒ Integer
readonly
Order No for sorting and comparison.
Conversion collapse
-
.from(value) ⇒ IS::Enum, ...
Converts various types to enum values.
-
.of(name) ⇒ IS::Enum
Strict lookup by name.
-
.parse(source) ⇒ IS::Enum
In specific enum class: get enum value by name; in Enum itself: parse string like “Class.name” to enum value.
-
#inspect ⇒ String
Detailed inspection string with class, name, order_no and attributes.
-
#to_s ⇒ String
Name as string.
-
#to_sym ⇒ Symbol
Name as symbol.
Collection collapse
-
.[](name_or_order) ⇒ IS::Enum?
Lookup by name or order number.
-
.aliases ⇒ Hash<Symbol, IS::Enum>
Hash of alias names to target values.
- .each ⇒ Enumerator, self
-
.first ⇒ IS::Enum?
First value by order_no, or nil if empty.
-
.last ⇒ IS::Enum?
Last value by order_no, or nil if empty.
-
.to_h ⇒ Hash<Symbol => IS::Enum>
Hash of all names and aliases.
-
.to_range ⇒ Range<IS::Enum>
Range from first to last value.
- .values ⇒ Array<IS::Enum>
DSL collapse
-
.define(name, order_no = nil, **attrs) ⇒ IS::Enum
protected
Defines new enum value or alias.
-
.finalize! ⇒ void
protected
Freezes internal structures, preventing further modifications.
Ordering collapse
-
#<=>(other) ⇒ Integer?
Returns
1if self > other;0if self == other;-1if self < other. -
#succ ⇒ IS::Enum?
Returns the next value by order_no, or nil if last.
Instance Attribute Details
#description ⇒ String? (readonly)
Returns optional value description.
287 288 289 |
# File 'lib/is-enum.rb', line 287 def description @description end |
#name ⇒ Symbol (readonly)
Returns value name.
284 285 286 |
# File 'lib/is-enum.rb', line 284 def name @name end |
#order_no ⇒ Integer (readonly)
Non-unique order numbers Multiple values may be defined with the same order_no. This affects sorting order (undefined when equal) and comparison behavior. See #<=> for comparison semantics.
Order No for sorting and comparison
281 282 283 |
# File 'lib/is-enum.rb', line 281 def order_no @order_no end |
Class Method Details
.[](name_or_order) ⇒ IS::Enum?
Lookup by name or order number. Returns nil if not found. See of for strict lookup that raises on missing value
117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/is-enum.rb', line 117 def [](name_or_order) case name_or_order when String, Symbol key = name_or_order.to_sym @values[key] || @aliases[key] when Integer @values.values.find { |v| v.order_no == name_or_order } else raise ArgumentError, "Invalid value for name or order_no: #{name_or_order.inspect}", caller_locations end end |
.aliases ⇒ Hash<Symbol, IS::Enum>
Returns hash of alias names to target values.
142 143 144 |
# File 'lib/is-enum.rb', line 142 def aliases @aliases end |
.define(name, order_no = nil, **attrs) ⇒ IS::Enum (protected)
Defines new enum value or alias.
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
# File 'lib/is-enum.rb', line 192 def define name, order_no = nil, **attrs @mutex ||= Thread::Mutex::new @mutex.synchronize do @sorted = nil @values ||= {} @aliases ||= {} case name when String name = name.to_sym when Symbol # do nothing else raise ArgumentError, "Invalid name: #{ name.inspect }", caller_locations end raise ArgumentError, "Duplicate value name: #{ name.inspect }", caller_locations if @values.has_key?(name) || @aliases.has_key?(name) als = attrs.delete :alias case als when self @aliases[name] = als define_singleton_method name do als end return als when Symbol, String als = als.to_sym als_value = @values[als] || @aliases[als] raise ArgumentError, "Invalid alias #{ als.inspect }: value not found", caller_locations unless als_value @aliases[name] = als_value define_singleton_method name do als_value end return als_value when nil # do nothing else raise ArgumentError, "Invalid alias value: #{ als.inspect }", caller_locations end case order_no when Integer # do nothing when nil order_no = (@values.values.map(&:order_no).max || 0) + 1 else raise ArgumentError, "Invalid order_no value: #{ order_no.inspect }", caller_locations end description = attrs.delete :description raise ArgumentError, "Invalid description value: #{ description.inspect }", caller_locations unless description.nil? || description.is_a?(String) value = new(order_no, name, description, **attrs).freeze @values[name] = value define_singleton_method name do value end value end end |
.each ⇒ Enumerator, self
130 131 132 133 134 |
# File 'lib/is-enum.rb', line 130 def each return to_enum(__method__) unless block_given? @values.values.sort_by { |v| v.order_no }.each { |v| yield v } self end |
.finalize! ⇒ void (protected)
This method returns an undefined value.
Freezes internal structures, preventing further modifications. After calling, define will raise RuntimeError.
252 253 254 255 256 257 258 |
# File 'lib/is-enum.rb', line 252 def finalize! @mutex ||= Thread::Mutex::new @mutex.synchronize do @values.freeze @aliases.freeze end end |
.first ⇒ IS::Enum?
Returns first value by order_no, or nil if empty.
152 153 154 |
# File 'lib/is-enum.rb', line 152 def first values.first end |
.from(value) ⇒ IS::Enum, ...
Converts various types to enum values.
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/is-enum.rb', line 89 def from value case value when nil nil when self value when Range Range::new from(value.begin), from(value.end), value.exclude_end? when Set Set[*value.map { |v| from(v) }] when Enumerable value.map { |v| from(v) } else result = self[value] raise ArgumentError, "Invalid value of #{ self }: #{ value.inspect }", caller_locations unless result result end end |
.last ⇒ IS::Enum?
Returns last value by order_no, or nil if empty.
147 148 149 |
# File 'lib/is-enum.rb', line 147 def last values.last end |
.of(name) ⇒ IS::Enum
Strict lookup by name. Raises if not found. See [] for lenient lookup
74 75 76 77 78 79 |
# File 'lib/is-enum.rb', line 74 def of name raise ArgumentError, "Invalid name of #{ self }: #{ name.inspect }" unless name.is_a?(Symbol) val = @values[name] || @aliases[name] raise ArgumentError, "Invalid name of #{ self }: #{ name }" unless val return val end |
.parse(source) ⇒ IS::Enum
Security consideration Converts strings to Symbols internally. Do not use with untrusted user input to avoid memory exhaustion from symbol creation.
In specific enum class: get enum value by name; in IS::Enum itself: parse string like “Class.name” to enum value.
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/is-enum.rb', line 51 def parse source raise ArgumentError, "Invalid source for parsing: #{ source.inspect }", caller_locations unless source.is_a?(String) if self == IS::Enum parts = source.split '.' raise ArgumentError, "Parsing error from #{ source.inspect }", caller_locations unless parts.is_a?(Array) && parts.size == 2 cls = @@enums[parts[0]] raise ArgumentError, "Enum class not found: #{ parts[0] }", caller_locations unless cls val = cls[parts[1].to_sym] raise ArgumentError, "#{ cls.name } value not found: #{ parts[1] }", caller_locations unless val return val else val = self[source.to_sym] raise ArgumentError, "#{ self.name } value not found: #{ source }", caller_locations unless val return val end end |
.to_h ⇒ Hash<Symbol => IS::Enum>
Both canonical names and aliases are included. To distinguish, check aliases for alias keys.
Returns hash of all names and aliases.
164 165 166 167 168 |
# File 'lib/is-enum.rb', line 164 def to_h result = {} result.merge! @values result.merge! @aliases end |
.to_range ⇒ Range<IS::Enum>
Returns range from first to last value.
157 158 159 |
# File 'lib/is-enum.rb', line 157 def to_range (first .. last) end |
.values ⇒ Array<IS::Enum>
137 138 139 |
# File 'lib/is-enum.rb', line 137 def values @sorted ||= @values.values.sort_by { |v| v.order_no } end |
Instance Method Details
#<=>(other) ⇒ Integer?
Comparison semantics ‘==` and `<=>` compare by order_no, while eql? compares object identity. Multiple values may share the same order_no; they compare as equal but are distinct objects.
class Alpha < IS::Enum
define :alpha, 10
define :beta, 20
define :bi, 20
define :Gamma, 30
define :g_letter, alias: :Gamma
end
Alpha.beta == Alpha.bi # => true (same order_no: 20)
Alpha.beta.eql?(Alpha.bi) # => false (different objects)
Alpha.Gamma.eql?(Alpha.g_letter) # => true (alias is same object)
Returns 1 if self > other; 0 if self == other; -1 if self < other. nil if other is not same type.
318 319 320 321 322 323 324 325 326 327 328 329 |
# File 'lib/is-enum.rb', line 318 def <=> other case other when self.class self.order_no <=> other.order_no when Symbol, String self.order_no <=> self.class[other.to_sym]&.order_no when Integer self.order_no <=> other else nil end end |
#inspect ⇒ String
Returns detailed inspection string with class, name, order_no and attributes.
353 354 355 356 357 358 359 360 |
# File 'lib/is-enum.rb', line 353 def inspect data = [ "#{ self.class }.#{ self.name }", "order_no=#{ @order_no }" ] data << "description=#{ @description.inspect }" if @description @attrs.each do |key, value| data << "#{ key }=#{ value.inspect }" end "[enum #{ data.join(' ') }]" end |
#succ ⇒ IS::Enum?
Returns the next value by order_no, or nil if last.
334 335 336 |
# File 'lib/is-enum.rb', line 334 def succ self.class.values.find { |v| v.order_no > self.order_no } end |
#to_s ⇒ String
Returns name as string.
348 349 350 |
# File 'lib/is-enum.rb', line 348 def to_s name.to_s end |
#to_sym ⇒ Symbol
Returns name as symbol.
343 344 345 |
# File 'lib/is-enum.rb', line 343 def to_sym name end |