lib/ruby-enum/enum.rb
# frozen_string_literal: true
module Ruby
module Enum
class << self
# Needed for I18n mock
attr_accessor :i18n
end
attr_reader :key, :value
def initialize(key, value)
@key = key
@value = value
end
def self.included(base)
base.extend Enumerable
base.extend ClassMethods
base.private_class_method(:new)
end
module ClassMethods
# Define an enumerated value.
#
# === Parameters
# [key] Enumerator key.
# [value] Enumerator value.
def define(key, value = key)
@_enum_hash ||= {}
@_enums_by_value ||= {}
validate_key!(key)
validate_value!(value)
store_new_instance(key, value)
if upper?(key.to_s)
const_set key, value
else
define_singleton_method(key) { value }
end
end
def store_new_instance(key, value)
new_instance = new(key, value)
@_enum_hash[key] = new_instance
@_enums_by_value[value] = new_instance
end
def const_missing(key)
raise Ruby::Enum::Errors::UninitializedConstantError, name: name, key: key
end
# Iterate over all enumerated values.
# Required for Enumerable mixin
def each(&block)
@_enum_hash.each(&block)
end
# Attempt to parse an enum key and return the
# corresponding value.
#
# === Parameters
# [k] The key string to parse.
#
# Returns the corresponding value or nil.
def parse(k)
k = k.to_s.upcase
each do |key, enum|
return enum.value if key.to_s.upcase == k
end
nil
end
# Whether the specified key exists in this enum.
#
# === Parameters
# [k] The string key to check.
#
# Returns true if the key exists, false otherwise.
def key?(k)
@_enum_hash.key?(k)
end
# Gets the string value for the specified key.
#
# === Parameters
# [k] The key symbol to get the value for.
#
# Returns the corresponding enum instance or nil.
def value(k)
enum = @_enum_hash[k]
enum&.value
end
# Whether the specified value exists in this enum.
#
# === Parameters
# [k] The string value to check.
#
# Returns true if the value exists, false otherwise.
def value?(v)
@_enums_by_value.key?(v)
end
# Gets the key symbol for the specified value.
#
# === Parameters
# [v] The string value to parse.
#
# Returns the corresponding key symbol or nil.
def key(v)
enum = @_enums_by_value[v]
enum&.key
end
# Returns all enum keys.
def keys
@_enum_hash.values.map(&:key)
end
# Returns all enum values.
def values
result = @_enum_hash.values.map(&:value)
if superclass < Ruby::Enum
superclass.values + result
else
result
end
end
# Iterate over all enumerated values.
# Required for Enumerable mixin
def each_value(&_block)
@_enum_hash.each_value do |v|
yield v.value
end
end
# Iterate over all enumerated keys.
# Required for Enumerable mixin
def each_key(&_block)
@_enum_hash.each_value do |v|
yield v.key
end
end
def to_h
@_enum_hash.transform_values(&:value)
end
private
def upper?(s)
!/[[:upper:]]/.match(s).nil?
end
def validate_key!(key)
return unless @_enum_hash.key?(key)
raise Ruby::Enum::Errors::DuplicateKeyError, name: name, key: key
end
def validate_value!(value)
return unless @_enums_by_value.key?(value)
raise Ruby::Enum::Errors::DuplicateValueError, name: name, value: value
end
end
end
end