Class: T::Enum
- Inherits:
-
Object
- Object
- T::Enum
- Defined in:
- lib/sorbet/eraser/t/enum.rb
Overview
This is mostly copy-pasted from sorbet-runtime since we have to maintain the same behavior.
Class Method Summary collapse
- ._load(args) ⇒ Object
-
._register_instance(instance) ⇒ Object
Maintains the order in which values are defined.
-
.deserialize(mongo_value) ⇒ Object
Note: Failed CriticalMethodsNoRuntimeTypingTest.
-
.each_value(&blk) ⇒ Object
This exists for compatibility with the interface of ‘Hash` & mostly to support the HashEachMethods Rubocop.
-
.enums(&blk) ⇒ Object
Entrypoint for allowing people to register new enum values.
-
.from_serialized(serialized_val) ⇒ self
Convert from serialized value to enum instance.
- .fully_initialized? ⇒ Boolean
-
.has_serialized?(serialized_val) ⇒ Boolean
Note: It would have been nice to make this method final before people started overriding it.
- .inherited(child_class) ⇒ Object
- .serialize(instance) ⇒ Object
- .started_initializing? ⇒ Boolean
-
.try_deserialize(serialized_val) ⇒ Object
Convert from serialized value to enum instance.
-
.values ⇒ Object
Enum class methods ##.
Instance Method Summary collapse
- #<=>(other) ⇒ Object
- #==(other) ⇒ Object
- #===(other) ⇒ Object
- #_bind_name(const_name) ⇒ Object
-
#_dump(_level) ⇒ Object
Marshal support.
- #clone ⇒ Object
-
#dup ⇒ Object
Enum instance methods ##.
-
#initialize(serialized_val = nil) ⇒ Enum
constructor
Private implementation ##.
- #inspect ⇒ Object
- #serialize ⇒ Object
- #to_json(*args) ⇒ Object
- #to_s ⇒ Object
-
#to_str ⇒ Object
NB: Do not call this method.
Constructor Details
permalink #initialize(serialized_val = nil) ⇒ Enum
Private implementation ##
146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/sorbet/eraser/t/enum.rb', line 146 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 = serialized_val @const_name = nil self.class._register_instance(self) end |
Class Method Details
permalink ._load(args) ⇒ Object
[View source]
248 249 250 |
# File 'lib/sorbet/eraser/t/enum.rb', line 248 def self._load(args) deserialize(Marshal.load(args)) end |
permalink ._register_instance(instance) ⇒ Object
Maintains the order in which values are defined
190 191 192 193 |
# File 'lib/sorbet/eraser/t/enum.rb', line 190 def self._register_instance(instance) @values ||= [] @values << instance end |
permalink .deserialize(mongo_value) ⇒ Object
Note: Failed CriticalMethodsNoRuntimeTypingTest
71 72 73 74 75 76 |
# File 'lib/sorbet/eraser/t/enum.rb', line 71 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 |
permalink .each_value(&blk) ⇒ Object
This exists for compatibility with the interface of ‘Hash` & mostly to support the HashEachMethods Rubocop.
19 20 21 22 23 24 25 |
# File 'lib/sorbet/eraser/t/enum.rb', line 19 def self.each_value(&blk) if blk values.each(&blk) else values.each end end |
permalink .enums(&blk) ⇒ Object
Entrypoint for allowing people to register new enum values. All enum values must be defined within this block.
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 |
# File 'lib/sorbet/eraser/t/enum.rb', line 197 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 = nil yield @mapping = nil @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 = @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 |
permalink .from_serialized(serialized_val) ⇒ self
Convert from serialized value to enum instance.
40 41 42 43 44 45 46 |
# File 'lib/sorbet/eraser/t/enum.rb', line 40 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 |
permalink .fully_initialized? ⇒ Boolean
185 186 187 |
# File 'lib/sorbet/eraser/t/enum.rb', line 185 def self.fully_initialized? @fully_initialized ||= false end |
permalink .has_serialized?(serialized_val) ⇒ Boolean
Note: It would have been nice to make this method final before people started overriding it.
50 51 52 53 54 55 56 |
# File 'lib/sorbet/eraser/t/enum.rb', line 50 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 |
permalink .inherited(child_class) ⇒ Object
[View source]
237 238 239 240 241 |
# File 'lib/sorbet/eraser/t/enum.rb', line 237 def self.inherited(child_class) super raise "Inheriting from children of T::Enum is prohibited" if self != T::Enum end |
permalink .serialize(instance) ⇒ Object
[View source]
58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/sorbet/eraser/t/enum.rb', line 58 def self.serialize(instance) 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 |
permalink .started_initializing? ⇒ Boolean
181 182 183 |
# File 'lib/sorbet/eraser/t/enum.rb', line 181 def self.started_initializing? @started_initializing ||= false end |
permalink .try_deserialize(serialized_val) ⇒ Object
Convert from serialized value to enum instance
28 29 30 31 32 33 34 |
# File 'lib/sorbet/eraser/t/enum.rb', line 28 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 |
permalink .values ⇒ Object
Enum class methods ##
9 10 11 12 13 14 15 |
# File 'lib/sorbet/eraser/t/enum.rb', line 9 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 |
Instance Method Details
permalink #<=>(other) ⇒ Object
[View source]
105 106 107 108 109 110 111 112 |
# File 'lib/sorbet/eraser/t/enum.rb', line 105 def <=>(other) case other when self.class self.serialize <=> other.serialize else nil end end |
permalink #==(other) ⇒ Object
[View source]
126 127 128 129 130 131 132 133 |
# File 'lib/sorbet/eraser/t/enum.rb', line 126 def ==(other) case other when String false else super(other) end end |
permalink #===(other) ⇒ Object
[View source]
135 136 137 138 139 140 141 142 |
# File 'lib/sorbet/eraser/t/enum.rb', line 135 def ===(other) case other when String false else super(other) end end |
permalink #_bind_name(const_name) ⇒ Object
[View source]
168 169 170 171 172 |
# File 'lib/sorbet/eraser/t/enum.rb', line 168 def _bind_name(const_name) @const_name = const_name @serialized_val = const_to_serialized_val(const_name) if @serialized_val.nil? freeze end |
permalink #_dump(_level) ⇒ Object
Marshal support
244 245 246 |
# File 'lib/sorbet/eraser/t/enum.rb', line 244 def _dump(_level) Marshal.dump(serialize) end |
permalink #clone ⇒ Object
[View source]
84 85 86 |
# File 'lib/sorbet/eraser/t/enum.rb', line 84 def clone self end |
permalink #dup ⇒ Object
Enum instance methods ##
80 81 82 |
# File 'lib/sorbet/eraser/t/enum.rb', line 80 def dup self end |
permalink #inspect ⇒ Object
[View source]
101 102 103 |
# File 'lib/sorbet/eraser/t/enum.rb', line 101 def inspect "#<#{self.class.name}::#{@const_name || '__UNINITIALIZED__'}>" end |
permalink #serialize ⇒ Object
[View source]
88 89 90 91 |
# File 'lib/sorbet/eraser/t/enum.rb', line 88 def serialize assert_bound! @serialized_val end |
permalink #to_json(*args) ⇒ Object
[View source]
93 94 95 |
# File 'lib/sorbet/eraser/t/enum.rb', line 93 def to_json(*args) serialize.to_json(*args) end |
permalink #to_s ⇒ Object
[View source]
97 98 99 |
# File 'lib/sorbet/eraser/t/enum.rb', line 97 def to_s inspect end |
permalink #to_str ⇒ Object
NB: Do not call this method. This exists to allow for a safe migration path in places where enum values are compared directly against string values.
Ruby’s string has a weird quirk where ‘’my_string’ == obj` calls obj.==(‘my_string’) if obj responds to the ‘to_str` method. It does not actually call `to_str` however.
121 122 123 124 |
# File 'lib/sorbet/eraser/t/enum.rb', line 121 def to_str msg = 'Implicit conversion of Enum instances to strings is not allowed. Call #serialize instead.' raise NoMethodError.new(msg) end |