class T::Enum

should be kept immutable to avoid unpredictable action at a distance.
WARNING: Enum instances are singletons that are shared among all their users. Their internals
def is_red?(suit); …; end
sig {params(suit: Suit).returns(Boolean)}
@example Using enums in type signatures:
Suit.deserialize(‘club’) == Suit::CLUB
@example Converting from serialized value to enum instance:
Suit::SPADE
@example Accessing values:
end
end
…
READY = new(‘rdy’)
enums do
class Status < T::Enum
@example Custom serialization value:
end
end
HEART = new
DIAMOND = new
SPADE = new
CLUB = new
enums do
class Suit < T::Enum
@example Declaring an Enum:
constructor. Enum will ‘freeze` the serialized value.
to lowercase (e.g. `Suit::Club.serialize == ’club’‘); however a custom value may be passed to the
Each value has a corresponding serialized value. By default this is the constant’s name converted
Every value is a singleton instance of the class (i.e. ‘Suit::SPADE.is_a?(Suit) == true`).
Enumerations allow for type-safe declarations of a fixed set of values.

def self._load(args)

def self._load(args)
  deserialize(Marshal.load(args)) # rubocop:disable Security/MarshalLoad
end

def self._register_instance(instance)

def self._register_instance(instance)
  @values ||= []
  @values << T.cast(instance, T.attached_class)
end

def self.deserialize(mongo_value)

def self.deserialize(mongo_value)
  if self == T::Enum
    raise "Cannot call T::Enum.deserialize directly. You must call on a specific child class."
  end
  self.from_serialized(mongo_value)
end

def self.each_value(&blk)

def self.each_value(&blk)
  if blk
    values.each(&blk)
  else
    values.each
  end
end

def self.enums(&blk)

def self.enums(&blk)
  raise "enums cannot be defined for T::Enum" if self == T::Enum
  raise "Enum #{self} was already initialized" if fully_initialized?
  raise "Enum #{self} is still initializing" if started_initializing?
  @started_initializing = true
  @values = T.let(nil, T.nilable(T::Array[T.attached_class]))
  yield
  @mapping = T.let(nil, T.nilable(T::Hash[SerializedVal, T.attached_class]))
  @mapping = {}
  # Freeze the Enum class and bind the constant names into each of the instances.
  self.constants(false).each do |const_name|
    instance = self.const_get(const_name, false)
    if !instance.is_a?(self)
      raise "Invalid constant #{self}::#{const_name} on enum. " \
        "All constants defined for an enum must be instances itself (e.g. `Foo = new`)."
    end
    instance._bind_name(const_name)
    serialized = instance.serialize
    if @mapping.include?(serialized)
      raise "Enum values must have unique serializations. Value '#{serialized}' is repeated on #{self}."
    end
    @mapping[serialized] = instance
  end
  @values.freeze
  @mapping.freeze
  orphaned_instances = T.must(@values) - @mapping.values
  if !orphaned_instances.empty?
    raise "Enum values must be assigned to constants: #{orphaned_instances.map {|v| v.instance_variable_get('@serialized_val')}}"
  end
  @fully_initialized = true
end

def self.from_serialized(serialized_val)

def self.from_serialized(serialized_val)
  res = try_deserialize(serialized_val)
  if res.nil?
    raise KeyError.new("Enum #{self} key not found: #{serialized_val.inspect}")
  end
  res
end

def self.fully_initialized?

def self.fully_initialized?
  unless defined?(@fully_initialized)
    @fully_initialized = T.let(false, T.nilable(T::Boolean))
  end
  T.must(@fully_initialized)
end

def self.has_serialized?(serialized_val)

def self.has_serialized?(serialized_val)
  if @mapping.nil?
    raise "Attempting to access serialization map of #{self.class} before it has been initialized." \
      " Enums are not initialized until the 'enums do' block they are defined in has finished running."
  end
  @mapping.include?(serialized_val)
end

def self.inherited(child_class)

def self.inherited(child_class)
  super
  raise "Inheriting from children of T::Enum is prohibited" if self != T::Enum
  # "oj" gem JSON support
  if Object.const_defined?(:Oj)
    Object.const_get(:Oj).register_odd(child_class, child_class, :try_deserialize, :serialize)
  end
end

def self.serialize(instance)

def self.serialize(instance)
  # This is needed otherwise if a Chalk::ODM::Document with a property of the shape
  # T::Hash[T.nilable(MyEnum), Integer] and a value that looks like {nil => 0} is
  # serialized, we throw the error on L102.
  return nil if instance.nil?
  if self == T::Enum
    raise "Cannot call T::Enum.serialize directly. You must call on a specific child class."
  end
  if instance.class != self
    raise "Cannot call #serialize on a value that is not an instance of #{self}."
  end
  instance.serialize
end

def self.started_initializing?

def self.started_initializing?
  unless defined?(@started_initializing)
    @started_initializing = T.let(false, T.nilable(T::Boolean))
  end
  T.must(@started_initializing)
end

def self.try_deserialize(serialized_val)

def self.try_deserialize(serialized_val)
  if @mapping.nil?
    raise "Attempting to access serialization map of #{self.class} before it has been initialized." \
      " Enums are not initialized until the 'enums do' block they are defined in has finished running."
  end
  @mapping[serialized_val]
end

def self.values

def self.values
  if @values.nil?
    raise "Attempting to access values of #{self.class} before it has been initialized." \
      " Enums are not initialized until the 'enums do' block they are defined in has finished running."
  end
  @values
end

def <=>(other)

def <=>(other)
  case other
  when self.class
    self.serialize <=> other.serialize
  else
    nil
  end
end

def ==(other)

def ==(other)
  case other
  when String
    if T::Configuration.legacy_t_enum_migration_mode?
      comparison_assertion_failed(:==, other)
      self.serialize == other
    else
      false
    end
  else
    super(other)
  end
end

def ===(other)

def ===(other)
  case other
  when String
    if T::Configuration.legacy_t_enum_migration_mode?
      comparison_assertion_failed(:===, other)
      self.serialize == other
    else
      false
    end
  else
    super(other)
  end
end

def _bind_name(const_name)

def _bind_name(const_name)
  @const_name = const_name
  @serialized_val = const_to_serialized_val(const_name) if @serialized_val.nil?
  freeze
end

def _dump(_level)

def _dump(_level)
  Marshal.dump(serialize)
end

def as_json(*args)

def as_json(*args)
  serialized_val = serialize
  return serialized_val unless serialized_val.respond_to?(:as_json)
  serialized_val.as_json(*args)
end

def assert_bound!

def assert_bound!
nst_name.nil?
e "Attempting to access Enum value on #{self.class} before it has been initialized." \
Enums are not initialized until the 'enums do' block they are defined in has finished running."

def clone

def clone
  self
end

def comparison_assertion_failed(method, other)

def comparison_assertion_failed(method, other)
figuration.soft_assert_handler(
m to string comparison not allowed. Compare to the Enum instance directly instead. See go/enum-migration',
ytime: {
ass: self.class.name,
lf: self.inspect,
her: other,
her_class: other.class.name,
thod: method,

def const_to_serialized_val(const_name)

def const_to_serialized_val(const_name)
orical note: We convert to lowercase names because the majority of existing calls to
e_accessible` were arrays of lowercase strings. Doing this conversion allowed for the
t amount of repetition in migrated declarations.
_name.to_s.downcase.freeze

def dup

def dup
  self
end

def initialize(serialized_val=nil)

def initialize(serialized_val=nil)
  raise 'T::Enum is abstract' if self.class == T::Enum
  if !self.class.started_initializing?
    raise "Must instantiate all enum values of #{self.class} inside 'enums do'."
  end
  if self.class.fully_initialized?
    raise "Cannot instantiate a new enum value of #{self.class} after it has been initialized."
  end
  serialized_val = serialized_val.frozen? ? serialized_val : serialized_val.dup.freeze
  @serialized_val = T.let(serialized_val, T.nilable(SerializedVal))
  @const_name = T.let(nil, T.nilable(Symbol))
  self.class._register_instance(self)
end

def inspect

def inspect
  "#<#{self.class.name}::#{@const_name || '__UNINITIALIZED__'}>"
end

def serialize

def serialize
  assert_bound!
  @serialized_val
end

def to_json(*args)

def to_json(*args)
  serialize.to_json(*args)
end

def to_s

def to_s
  inspect
end

def to_str

def to_str
  msg = 'Implicit conversion of Enum instances to strings is not allowed. Call #serialize instead.'
  if T::Configuration.legacy_t_enum_migration_mode?
    T::Configuration.soft_assert_handler(
      msg,
      storytime: {class: self.class.name},
    )
    serialize.to_s
  else
    raise NoMethodError.new(msg)
  end
end