sorbet-runtime 0.4.4969 → 0.4.4972

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 43f204f6d778bf65a3c81518f5ac3c2160b74f4bcd0ea73654285fa9561d7884
4
- data.tar.gz: d897c48135338d0edaaf7562ca4de202c0c139e124d041bc29fb08485162107f
3
+ metadata.gz: 53211ac8c4d54178673a7fb13f499fea6e0808239266f249027303fedab70fb7
4
+ data.tar.gz: b5952024c57f88f975fb689e8b921b283eb4a20bf56b04380b6c8cf3236a968e
5
5
  SHA512:
6
- metadata.gz: ce7b1756acfbc8a10b38550642a5f2af8bade745b80bb88dfe6278659f2a8d48589cd8b56d6a537f799d94098d91f817d3a96227223d1432f5ec342ded73c51d
7
- data.tar.gz: dde92e7a96749903b11bd41485859200c06bed34de58b0cba887204560b9602838592baeb0964675ed5216e6f0887b400cee9f2248367dca945c2999086196fe
6
+ metadata.gz: f0aed40e7e7617d289e77544f30a2ed8335ea1a4480159b75194622cd777897c64630f417b9f35dc99571e9c8818e3e76f9227ea0ed90ba3625463524e3fc75a
7
+ data.tar.gz: 2f21287bacccc1557d1fb5c4c257c7c34259724e2306a5db27c98620841af4a643e004913a79f421ca2d760003cdf1b88081346c4c2b85184eeb5eef8582b90b
@@ -99,6 +99,8 @@ require_relative 'types/props/constructor'
99
99
  require_relative 'types/props/pretty_printable'
100
100
  require_relative 'types/props/serializable'
101
101
  require_relative 'types/props/type_validation'
102
+
102
103
  require_relative 'types/struct'
104
+ require_relative 'types/enum'
103
105
 
104
106
  require_relative 'types/compatibility_patches'
@@ -356,6 +356,7 @@ module T::Configuration
356
356
  String
357
357
  Symbol
358
358
  Time
359
+ T::Enum
359
360
  }).freeze
360
361
 
361
362
  def self.scalar_types
@@ -382,6 +383,15 @@ module T::Configuration
382
383
  end
383
384
  end
384
385
 
386
+ def self.enable_legacy_t_enum_migration_mode
387
+ @legacy_t_enum_migration_mode = true
388
+ end
389
+ def self.disable_legacy_t_enum_migration_mode
390
+ @legacy_t_enum_migration_mode = false
391
+ end
392
+ def self.legacy_t_enum_migration_mode?
393
+ @legacy_t_enum_migration_mode || false
394
+ end
385
395
 
386
396
  private_class_method def self.validate_lambda_given!(value)
387
397
  if !value.nil? && !value.respond_to?(:call)
data/lib/types/enum.rb ADDED
@@ -0,0 +1,334 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ # Enumerations allow for type-safe declarations of a fixed set of values.
5
+ #
6
+ # Every value is a singleton instance of the class (i.e. `Suit::SPADE.is_a?(Suit) == true`).
7
+ #
8
+ # Each value has a corresponding serialized value. By default this is the constant's name converted
9
+ # to lowercase (e.g. `Suit::Club.serialize == 'club'`); however a custom value may be passed to the
10
+ # constructor. Enum will `freeze` the serialized value.
11
+ #
12
+ # @example Declaring an Enum:
13
+ # class Suit < T::Enum
14
+ # enums do
15
+ # CLUB = T.let(new, Suit)
16
+ # SPADE = T.let(new, Suit)
17
+ # DIAMOND = T.let(new, Suit)
18
+ # HEART = T.let(new, Suit)
19
+ # end
20
+ # end
21
+ #
22
+ # @example Custom serialization value:
23
+ # class Status < T::Enum
24
+ # enums do
25
+ # READY = T.let(new('rdy'), Status)
26
+ # ...
27
+ # end
28
+ # end
29
+ #
30
+ # @example Accessing values:
31
+ # Suit::SPADE
32
+ #
33
+ # @example Converting from serialized value to enum instance:
34
+ # Suit.from_serialized('club') == Suit::CLUB
35
+ #
36
+ # @example Using enums in type signatures:
37
+ # sig {params(suit: Suit).returns(Boolean)}
38
+ # def is_red?(suit); ...; end
39
+ #
40
+ # WARNING: Enum instances are singletons that are shared among all their users. Their internals
41
+ # should be kept immutable to avoid unpredictable action at a distance.
42
+ class T::Enum
43
+ extend T::Sig
44
+ extend T::Props::CustomType
45
+
46
+ # TODO(jez) Might want to restrict this, or make subclasses provide this type
47
+ SerializedVal = T.type_alias {T.untyped}
48
+ private_constant :SerializedVal
49
+
50
+ ## Enum class methods ##
51
+ sig {returns(T::Array[T.experimental_attached_class])}
52
+ def self.values
53
+ if @values.nil?
54
+ raise "Attempting to access values of #{self.class} before it has been initialized." \
55
+ " Enums are not initialized until the 'enums do' block they are defined in has finished running."
56
+ end
57
+ @values
58
+ end
59
+
60
+ # Convert from serialized value to enum instance.
61
+ #
62
+ # Note: It would have been nice to make this method final before people started overriding it.
63
+ # Note: Failed CriticalMethodsNoRuntimeTypingTest
64
+ #
65
+ # @return [self]
66
+ # @raise [KeyError] if serialized value does not match any instance.
67
+ sig {overridable.params(serialized_val: SerializedVal).returns(T.experimental_attached_class).checked(:never)}
68
+ def self.from_serialized(serialized_val)
69
+ if @mapping.nil?
70
+ raise "Attempting to access serialization map of #{self.class} before it has been initialized." \
71
+ " Enums are not initialized until the 'enums do' block they are defined in has finished running."
72
+ end
73
+ res = @mapping[serialized_val]
74
+ if res.nil?
75
+ raise KeyError.new("Enum #{self} key not found: #{serialized_val.inspect}")
76
+ end
77
+ res
78
+ end
79
+
80
+ # Note: It would have been nice to make this method final before people started overriding it.
81
+ # @return [Boolean] Does the given serialized value correspond with any of this enum's values.
82
+ sig {overridable.params(serialized_val: SerializedVal).returns(T::Boolean)}
83
+ def self.has_serialized?(serialized_val)
84
+ if @mapping.nil?
85
+ raise "Attempting to access serialization map of #{self.class} before it has been initialized." \
86
+ " Enums are not initialized until the 'enums do' block they are defined in has finished running."
87
+ end
88
+ @mapping.include?(serialized_val)
89
+ end
90
+
91
+
92
+ ## T::Props::CustomType
93
+
94
+ # Note: Failed CriticalMethodsNoRuntimeTypingTest
95
+ sig {params(value: T.untyped).returns(T::Boolean).checked(:never)}
96
+ def self.instance?(value)
97
+ value.is_a?(self)
98
+ end
99
+
100
+ # Note: Failed CriticalMethodsNoRuntimeTypingTest
101
+ sig {params(instance: T.nilable(T::Enum)).returns(SerializedVal).checked(:never)}
102
+ def self.serialize(instance)
103
+ # This is needed otherwise if a Chalk::ODM::Document with a property of the shape
104
+ # T::Hash[T.nilable(MyEnum), Integer] and a value that looks like {nil => 0} is
105
+ # serialized, we throw the error on L102.
106
+ return nil if instance.nil?
107
+
108
+ if self == T::Enum
109
+ raise "Cannot call T::Enum.serialize directly. You must call on a specific child class."
110
+ end
111
+ if instance.class != self
112
+ raise "Cannot call #serialize on a value that is not an instance of #{self}."
113
+ end
114
+ instance.serialize
115
+ end
116
+
117
+ # Note: Failed CriticalMethodsNoRuntimeTypingTest
118
+ sig {params(mongo_value: SerializedVal).returns(T.experimental_attached_class).checked(:never)}
119
+ def self.deserialize(mongo_value)
120
+ if self == T::Enum
121
+ raise "Cannot call T::Enum.deserialize directly. You must call on a specific child class."
122
+ end
123
+ self.from_serialized(mongo_value)
124
+ end
125
+
126
+
127
+ ## Enum instance methods ##
128
+
129
+
130
+ sig {returns(T.self_type)}
131
+ def dup
132
+ self
133
+ end
134
+
135
+ sig {returns(T.self_type).checked(:tests)}
136
+ def clone
137
+ self
138
+ end
139
+
140
+ # Note: Failed CriticalMethodsNoRuntimeTypingTest
141
+ sig {returns(SerializedVal).checked(:never)}
142
+ def serialize
143
+ assert_bound!
144
+ @serialized_val
145
+ end
146
+
147
+ sig {params(args: T.untyped).returns(T.untyped)}
148
+ def to_json(*args)
149
+ serialize.to_json(*args)
150
+ end
151
+
152
+ sig {returns(String)}
153
+ def to_s
154
+ inspect
155
+ end
156
+
157
+ sig {returns(String)}
158
+ def inspect
159
+ "#<#{self.class.name}::#{@const_name || '__UNINITIALIZED__'}>"
160
+ end
161
+
162
+ sig {params(other: BasicObject).returns(T.nilable(Integer))}
163
+ def <=>(other)
164
+ case other
165
+ when self.class
166
+ self.serialize <=> other.serialize
167
+ else
168
+ nil
169
+ end
170
+ end
171
+
172
+
173
+ # NB: Do not call this method. This exists to allow for a safe migration path in places where enum
174
+ # values are compared directly against string values.
175
+ #
176
+ # Ruby's string has a weird quirk where `'my_string' == obj` calls obj.==('my_string') if obj
177
+ # responds to the `to_str` method. It does not actually call `to_str` however.
178
+ #
179
+ # See https://ruby-doc.org/core-2.4.0/String.html#method-i-3D-3D
180
+ sig {returns(String)}
181
+ def to_str
182
+ msg = 'Implicit conversion of Enum instances to strings is not allowed. Call #serialize instead.'
183
+ if T::Configuration.legacy_t_enum_migration_mode?
184
+ T::Configuration.soft_assert_handler(
185
+ msg,
186
+ storytime: {class: self.class.name},
187
+ )
188
+ serialize.to_s
189
+ else
190
+ raise NoMethodError.new(msg)
191
+ end
192
+ end
193
+
194
+ sig {params(other: BasicObject).returns(T::Boolean).checked(:never)}
195
+ def ==(other)
196
+ case other
197
+ when String
198
+ if T::Configuration.legacy_t_enum_migration_mode?
199
+ comparison_assertion_failed(:==, other)
200
+ self.serialize == other
201
+ else
202
+ false
203
+ end
204
+ else
205
+ super(other)
206
+ end
207
+ end
208
+
209
+ sig {params(other: BasicObject).returns(T::Boolean).checked(:never)}
210
+ def ===(other)
211
+ case other
212
+ when String
213
+ if T::Configuration.legacy_t_enum_migration_mode?
214
+ comparison_assertion_failed(:===, other)
215
+ self.serialize == other
216
+ else
217
+ false
218
+ end
219
+ else
220
+ super(other)
221
+ end
222
+ end
223
+
224
+ sig {params(method: Symbol, other: T.untyped).void}
225
+ private def comparison_assertion_failed(method, other)
226
+ T::Configuration.soft_assert_handler(
227
+ 'Enum to string comparison not allowed. Compare to the Enum instance directly instead. See go/enum-migration',
228
+ storytime: {
229
+ class: self.class.name,
230
+ self: self.inspect,
231
+ other: other,
232
+ other_class: other.class.name,
233
+ method: method,
234
+ }
235
+ )
236
+ end
237
+
238
+
239
+ ## Private implementation ##
240
+
241
+
242
+ sig {params(serialized_val: SerializedVal).void}
243
+ private def initialize(serialized_val=nil)
244
+ raise 'T::Enum is abstract' if self.class == T::Enum
245
+ if !self.class.started_initializing?
246
+ raise "Must instantiate all enum values of #{self.class} inside 'enums do'."
247
+ end
248
+ if self.class.fully_initialized?
249
+ raise "Cannot instantiate a new enum value of #{self.class} after it has been initialized."
250
+ end
251
+
252
+ serialized_val = serialized_val.frozen? ? serialized_val : serialized_val.dup.freeze
253
+ @serialized_val = T.let(serialized_val, T.nilable(SerializedVal))
254
+ @const_name = T.let(nil, T.nilable(Symbol))
255
+ end
256
+
257
+ sig {returns(NilClass).checked(:never)}
258
+ private def assert_bound!
259
+ if @const_name.nil?
260
+ raise "Attempting to access Enum value on #{self.class} before it has been initialized." \
261
+ " Enums are not initialized until the 'enums do' block they are defined in has finished running."
262
+ end
263
+ end
264
+
265
+ sig {params(const_name: Symbol).void}
266
+ def _bind_name(const_name)
267
+ @const_name = const_name
268
+ @serialized_val = const_to_serialized_val(const_name) if @serialized_val.nil?
269
+ freeze
270
+ end
271
+
272
+ sig {params(const_name: Symbol).returns(String)}
273
+ private def const_to_serialized_val(const_name)
274
+ # Historical note: We convert to lowercase names because the majority of existing calls to
275
+ # `make_accessible` were arrays of lowercase strings. Doing this conversion allowed for the
276
+ # least amount of repetition in migrated declarations.
277
+ const_name.to_s.downcase.freeze
278
+ end
279
+
280
+ sig {returns(T::Boolean)}
281
+ def self.started_initializing?
282
+ @started_initializing = T.let(@started_initializing, T.nilable(T::Boolean))
283
+ @started_initializing ||= false
284
+ end
285
+
286
+ sig {returns(T::Boolean)}
287
+ def self.fully_initialized?
288
+ @fully_initialized = T.let(@fully_initialized, T.nilable(T::Boolean))
289
+ @fully_initialized ||= false
290
+ end
291
+
292
+ # Entrypoint for allowing people to register new enum values.
293
+ # All enum values must be defined within this block.
294
+ sig {params(blk: T.proc.void).void}
295
+ def self.enums(&blk)
296
+ raise "enums cannot be defined for T::Enum" if self == T::Enum
297
+ raise "Enum #{self} was already initialized" if @fully_initialized
298
+ raise "Enum #{self} is still initializing" if @started_initializing
299
+
300
+ @started_initializing = true
301
+
302
+ yield
303
+
304
+ @values = T.let(nil, T.nilable(T::Array[T.experimental_attached_class]))
305
+ @mapping = T.let(nil, T.nilable(T::Hash[SerializedVal, T.experimental_attached_class]))
306
+
307
+ # Freeze the Enum class and bind the constant names into each of the instances.
308
+ @mapping = {}
309
+ self.constants(false).each do |const_name|
310
+ instance = self.const_get(const_name, false)
311
+ if !instance.is_a?(self)
312
+ raise "Invalid constant #{self}::#{const_name} on enum. " \
313
+ "All constants defined for an enum must be instances itself (e.g. `Foo = new`)."
314
+ end
315
+
316
+ instance._bind_name(const_name)
317
+ serialized = instance.serialize
318
+ if @mapping.include?(serialized)
319
+ raise "Enum values must have unique serializations. Value '#{serialized}' is repeated on #{self}."
320
+ end
321
+ @mapping[serialized] = instance
322
+ end
323
+ @values = @mapping.values.sort.freeze
324
+ @mapping.freeze
325
+ @fully_initialized = true
326
+ end
327
+
328
+ sig {params(child_class: Module).void}
329
+ def self.inherited(child_class)
330
+ super
331
+
332
+ raise "Inheriting from children of T::Enum is prohibited" if self != T::Enum
333
+ end
334
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sorbet-runtime
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.4969
4
+ version: 0.4.4972
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stripe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-11-02 00:00:00.000000000 Z
11
+ date: 2019-11-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -78,6 +78,7 @@ files:
78
78
  - lib/types/boolean.rb
79
79
  - lib/types/compatibility_patches.rb
80
80
  - lib/types/configuration.rb
81
+ - lib/types/enum.rb
81
82
  - lib/types/generic.rb
82
83
  - lib/types/helpers.rb
83
84
  - lib/types/interface_wrapper.rb