sorbet-coerce 0.4.0 → 0.7.0

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: 7503ed812a03fc5478701f1821be08195b10945d700dd27cda21088aa2e01f55
4
- data.tar.gz: 6fd2a30012fad3af9f5fceee3de2a60c03bc7fb3f7fccdf42223b669c0e8c409
3
+ metadata.gz: 5831cb764479cf2f531e1f8b9ff26b189cfc7783ab0075f75aaa6e1a5bc5ac13
4
+ data.tar.gz: e9fd50d576cebbd6120e84c7c06db5e60828d3ad067fb374dd27d07b97b140a8
5
5
  SHA512:
6
- metadata.gz: ebbeb777352a39fe67575ee44c12ccb4ad1433ff7504bd76a9f9b86d91aef09a6e120496763254f433af783e2ab6dfec17b7822651db1bf563f9b7f223e8149c
7
- data.tar.gz: 7d536c441a095d32af634b7ad362750e9c6d4f95789cf9cfdeb3d77aa0a90485ca4622517d3138cf7815c9eadc0e282ef7e75711b5fde27d4bfcca75b561dba8
6
+ metadata.gz: df61492bbd7352033ee05db2b589078fe791b9a9f2b0e18539e89557a7a58c8f77b6f87fcf6aca3fb3b94933dc350b437738a68aee42e9214d946f177ea24e8e
7
+ data.tar.gz: 97356a851a7629866bdccb99beed3bb36a8820add245688161c8cbc0c734bfeabd5884794cb712d6090268f965f8a98df1ff7886674ed592695518f3e56c4956
@@ -9,8 +9,8 @@ module TypeCoerce
9
9
 
10
10
  Elem = type_member
11
11
 
12
- sig { params(args: T.untyped, raise_coercion_error: T.nilable(T::Boolean)).returns(Elem) }
13
- def from(args, raise_coercion_error: nil); end
12
+ sig { params(args: T.untyped, raise_coercion_error: T.nilable(T::Boolean), coerce_empty_to_nil: T::Boolean).returns(Elem) }
13
+ def from(args, raise_coercion_error: nil, coerce_empty_to_nil: false); end
14
14
 
15
15
  class CoercionError < SafeType::CoercionError; end
16
16
  class ShapeError < SafeType::CoercionError; end
@@ -25,12 +25,12 @@ class TypeCoerce::Converter
25
25
  "#{name}#[#{@type.to_s}]"
26
26
  end
27
27
 
28
- def from(args, raise_coercion_error: nil)
28
+ def from(args, raise_coercion_error: nil, coerce_empty_to_nil: false)
29
29
  if raise_coercion_error.nil?
30
30
  raise_coercion_error = TypeCoerce::Configuration.raise_coercion_error
31
31
  end
32
32
 
33
- T.let(_convert(args, @type, raise_coercion_error), @type)
33
+ T.let(_convert(args, @type, raise_coercion_error, coerce_empty_to_nil), @type)
34
34
  end
35
35
 
36
36
  private
@@ -45,15 +45,19 @@ class TypeCoerce::Converter
45
45
  Time,
46
46
  ], T.untyped)
47
47
 
48
- def _convert(value, type, raise_coercion_error)
48
+ def _convert(value, type, raise_coercion_error, coerce_empty_to_nil)
49
49
  if type.is_a?(T::Types::Untyped)
50
50
  value
51
+ elsif type.is_a?(T::Types::ClassOf)
52
+ value
51
53
  elsif type.is_a?(T::Types::TypedArray)
52
- _convert_to_a(value, type.type, raise_coercion_error)
54
+ _convert_to_a(value, type.type, raise_coercion_error, coerce_empty_to_nil)
55
+ elsif type.is_a?(T::Types::FixedArray)
56
+ _convert_to_a(value, type.types, raise_coercion_error, coerce_empty_to_nil)
53
57
  elsif type.is_a?(T::Types::TypedSet)
54
- Set.new(_convert_to_a(value, type.type, raise_coercion_error))
58
+ Set.new(_convert_to_a(value, type.type, raise_coercion_error, coerce_empty_to_nil))
55
59
  elsif type.is_a?(T::Types::Simple)
56
- _convert(value, type.raw_type, raise_coercion_error)
60
+ _convert(value, type.raw_type, raise_coercion_error, coerce_empty_to_nil)
57
61
  elsif type.is_a?(T::Types::Union)
58
62
  true_idx = T.let(nil, T.nilable(Integer))
59
63
  false_idx = T.let(nil, T.nilable(Integer))
@@ -65,9 +69,7 @@ class TypeCoerce::Converter
65
69
  false_idx = i if t.is_a?(T::Types::Simple) && t.raw_type == FalseClass
66
70
  end
67
71
 
68
- raise ArgumentError.new(
69
- 'the only supported union types are T.nilable and T::Boolean',
70
- ) unless (
72
+ return value unless (
71
73
  (!true_idx.nil? && !false_idx.nil? && !nil_idx.nil?) || # T.nilable(T::Boolean)
72
74
  (type.types.length == 2 && (
73
75
  !nil_idx.nil? || (!true_idx.nil? && !false_idx.nil?) # T.nilable || T::Boolean
@@ -75,12 +77,12 @@ class TypeCoerce::Converter
75
77
  )
76
78
 
77
79
  if !true_idx.nil? && !false_idx.nil?
78
- _convert_simple(value, T::Boolean, raise_coercion_error)
80
+ _convert_simple(value, T::Boolean, raise_coercion_error, coerce_empty_to_nil)
79
81
  else
80
- _convert(value, type.types[nil_idx == 0 ? 1 : 0], raise_coercion_error)
82
+ _convert(value, type.types[nil_idx == 0 ? 1 : 0], raise_coercion_error, coerce_empty_to_nil)
81
83
  end
82
84
  elsif type.is_a?(T::Types::TypedHash)
83
- return {} if _nil_like?(value, type)
85
+ return {} if _nil_like?(value, type, coerce_empty_to_nil)
84
86
 
85
87
  unless value.respond_to?(:map)
86
88
  raise TypeCoerce::ShapeError.new(value, type)
@@ -88,30 +90,34 @@ class TypeCoerce::Converter
88
90
 
89
91
  value.map do |k, v|
90
92
  [
91
- _convert(k, type.keys, raise_coercion_error),
92
- _convert(v, type.values, raise_coercion_error),
93
+ _convert(k, type.keys, raise_coercion_error, coerce_empty_to_nil),
94
+ _convert(v, type.values, raise_coercion_error, coerce_empty_to_nil),
93
95
  ]
94
96
  end.to_h
95
97
  elsif Object.const_defined?('T::Private::Types::TypeAlias') &&
96
98
  type.is_a?(T::Private::Types::TypeAlias)
97
- _convert(value, type.aliased_type, raise_coercion_error)
98
- elsif type.respond_to?(:<) && type < T::Struct
99
- return value if value.is_a?(type)
100
-
101
- args = _build_args(value, type, raise_coercion_error)
102
- type.new(args)
103
- elsif type.respond_to?(:<) && type < T::Enum
104
- return value if value.is_a?(type)
105
-
106
- _convert_enum(value, type, raise_coercion_error)
99
+ _convert(value, type.aliased_type, raise_coercion_error, coerce_empty_to_nil)
100
+ elsif type.is_a?(Class) || type.is_a?(Module)
101
+ return coerce_nil(value, type, coerce_empty_to_nil) if value.is_a?(type)
102
+
103
+ if type < T::Struct
104
+ args = _build_args(value, type, raise_coercion_error, coerce_empty_to_nil)
105
+ type.new(args)
106
+ elsif type < T::Enum
107
+ _convert_enum(value, type, raise_coercion_error, coerce_empty_to_nil)
108
+ else
109
+ _convert_simple(value, type, raise_coercion_error, coerce_empty_to_nil)
110
+ end
107
111
  else
108
- return value if value.is_a?(type)
109
-
110
- _convert_simple(value, type, raise_coercion_error)
112
+ if raise_coercion_error
113
+ raise TypeCoerce::CoercionError.new(value, type)
114
+ else
115
+ value
116
+ end
111
117
  end
112
118
  end
113
119
 
114
- def _convert_enum(value, type, raise_coercion_error)
120
+ def _convert_enum(value, type, raise_coercion_error, coerce_empty_to_nil)
115
121
  if raise_coercion_error
116
122
  type.deserialize(value)
117
123
  else
@@ -121,8 +127,8 @@ class TypeCoerce::Converter
121
127
  raise TypeCoerce::CoercionError.new(value, type)
122
128
  end
123
129
 
124
- def _convert_simple(value, type, raise_coercion_error)
125
- return nil if _nil_like?(value, type)
130
+ def _convert_simple(value, type, raise_coercion_error, coerce_empty_to_nil)
131
+ return nil if _nil_like?(value, type, coerce_empty_to_nil)
126
132
 
127
133
  safe_type_rule = T.let(nil, T.untyped)
128
134
 
@@ -130,6 +136,8 @@ class TypeCoerce::Converter
130
136
  safe_type_rule = SafeType::Boolean.strict
131
137
  elsif value.is_a?(type)
132
138
  return value
139
+ elsif type == BigDecimal
140
+ return BigDecimal(value)
133
141
  elsif PRIMITIVE_TYPES.include?(type)
134
142
  safe_type_rule = SafeType.const_get(type.name).strict
135
143
  else
@@ -149,18 +157,24 @@ class TypeCoerce::Converter
149
157
  end
150
158
  end
151
159
 
152
- def _convert_to_a(ary, type, raise_coercion_error)
153
- return [] if _nil_like?(ary, type)
160
+ def _convert_to_a(ary, type, raise_coercion_error, coerce_empty_to_nil)
161
+ return [] if _nil_like?(ary, type, coerce_empty_to_nil)
154
162
 
155
163
  unless ary.respond_to?(:map)
156
164
  raise TypeCoerce::ShapeError.new(ary, type)
157
165
  end
158
166
 
159
- ary.map { |value| _convert(value, type, raise_coercion_error) }
167
+ ary.map.with_index do |value, i|
168
+ if type.is_a?(Array)
169
+ _convert(value, type[i], raise_coercion_error, coerce_empty_to_nil)
170
+ else
171
+ _convert(value, type, raise_coercion_error, coerce_empty_to_nil)
172
+ end
173
+ end
160
174
  end
161
175
 
162
- def _build_args(args, type, raise_coercion_error)
163
- return {} if _nil_like?(args, Hash)
176
+ def _build_args(args, type, raise_coercion_error, coerce_empty_to_nil)
177
+ return {} if _nil_like?(args, Hash, coerce_empty_to_nil)
164
178
 
165
179
  unless args.respond_to?(:each_pair)
166
180
  raise TypeCoerce::ShapeError.new(args, type)
@@ -172,12 +186,22 @@ class TypeCoerce::Converter
172
186
  [
173
187
  key,
174
188
  (!props.include?(key) || value.nil?) ?
175
- nil : _convert(value, props[key][:type], raise_coercion_error),
189
+ nil : _convert(value, props[key][:type], raise_coercion_error, coerce_empty_to_nil),
176
190
  ]
177
191
  }.to_h.slice(*props.keys)
178
192
  end
179
193
 
180
- def _nil_like?(value, type)
181
- value.nil? || (value == '' && type != String)
194
+ def _nil_like?(value, type, coerce_empty_to_nil)
195
+ return true if value.nil?
196
+ return true if value == '' && coerce_empty_to_nil
197
+ return true if value == '' && type != String
198
+
199
+ false
200
+ end
201
+
202
+ def coerce_nil(value, type, coerce_empty_to_nil)
203
+ return nil if _nil_like?(value, type, coerce_empty_to_nil)
204
+
205
+ value
182
206
  end
183
207
  end
data/spec/coerce_spec.rb CHANGED
@@ -19,6 +19,7 @@ describe TypeCoerce do
19
19
  class Param < T::Struct
20
20
  const :id, Integer
21
21
  const :role, String, default: 'wizard'
22
+ const :price, BigDecimal
22
23
  const :info, ParamInfo
23
24
  const :opt, T.nilable(ParamInfo2)
24
25
  end
@@ -46,6 +47,14 @@ describe TypeCoerce do
46
47
  const :myenum, TestEnum
47
48
  end
48
49
 
50
+ class WithNilableString < T::Struct
51
+ const :myvalue, T.nilable(String)
52
+ end
53
+
54
+ class WithNilableInteger < T::Struct
55
+ const :myvalue, T.nilable(Integer)
56
+ end
57
+
49
58
  class CustomType
50
59
  attr_reader :a
51
60
 
@@ -62,9 +71,26 @@ describe TypeCoerce do
62
71
  # Does not respond to new
63
72
  end
64
73
 
74
+ class WithSupportedUnion < T::Struct
75
+ const :nilable, T.nilable(String)
76
+ const :nilable_boolean, T.nilable(T::Boolean)
77
+ end
78
+
79
+ class WithUnsupportedUnion < T::Struct
80
+ const :union, T.any(String, Integer)
81
+ end
82
+
83
+ class WithFixedArray < T::Struct
84
+ const :arr, [Integer, String, Integer]
85
+ end
86
+
87
+ class CustomString < String
88
+ end
89
+
65
90
  let!(:param) {
66
91
  TypeCoerce[Param].new.from({
67
92
  id: 1,
93
+ price: BigDecimal('98.76'),
68
94
  info: {
69
95
  name: 'mango',
70
96
  lvl: 100,
@@ -81,6 +107,7 @@ describe TypeCoerce do
81
107
  let!(:param2) {
82
108
  TypeCoerce[Param].new.from({
83
109
  id: '2',
110
+ price: '98.76',
84
111
  info: {
85
112
  name: 'honeydew',
86
113
  lvl: '5',
@@ -97,6 +124,7 @@ describe TypeCoerce do
97
124
  it 'reveals the right type' do
98
125
  T.assert_type!(param, Param)
99
126
  T.assert_type!(param.id, Integer)
127
+ T.assert_type!(param.price, BigDecimal)
100
128
  T.assert_type!(param.info, ParamInfo)
101
129
  T.assert_type!(param.info.name,String)
102
130
  T.assert_type!(param.info.lvl, Integer)
@@ -106,6 +134,7 @@ describe TypeCoerce do
106
134
  it 'coerces correctly' do
107
135
  expect(param.id).to eql 1
108
136
  expect(param.role).to eql 'wizard'
137
+ expect(param.price).to eql BigDecimal('98.76')
109
138
  expect(param.info.lvl).to eql 100
110
139
  expect(param.info.name).to eql 'mango'
111
140
  expect(param.info.skill_ids).to eql [123, 456]
@@ -113,6 +142,7 @@ describe TypeCoerce do
113
142
  expect(TypeCoerce[Param].new.from(param)).to eq(param)
114
143
 
115
144
  expect(param2.id).to eql 2
145
+ expect(param2.price).to eql BigDecimal('98.76')
116
146
  expect(param2.info.name).to eql 'honeydew'
117
147
  expect(param2.info.lvl).to eql 5
118
148
  expect(param2.info.skill_ids).to eql []
@@ -138,12 +168,12 @@ describe TypeCoerce do
138
168
  context 'when the given T::Struct is invalid' do
139
169
  class Param2 < T::Struct
140
170
  const :id, Integer
141
- const :info, T.any(Integer, String)
171
+ const :param, Param
142
172
  end
143
173
 
144
174
  it 'raises an error' do
145
175
  expect {
146
- TypeCoerce[Param2].new.from({id: 1, info: 1})
176
+ TypeCoerce[Param2].new.from({id: 1, info: {}})
147
177
  }.to raise_error(ArgumentError)
148
178
  end
149
179
  end
@@ -186,7 +216,34 @@ describe TypeCoerce do
186
216
  end
187
217
  end
188
218
 
189
- context 'when dealing with arries' do
219
+ context 'when given union types' do
220
+ context 'supported union types' do
221
+ it 'coerces correctly' do
222
+ coerced = TypeCoerce[WithSupportedUnion].new.from({
223
+ nilable: 2,
224
+ nilable_boolean: 'true'
225
+ })
226
+ expect(coerced.nilable).to eq('2')
227
+ expect(coerced.nilable_boolean).to eq(true)
228
+ end
229
+ end
230
+
231
+ context 'unsupported union types' do
232
+ it 'keeps the values as-is' do
233
+ coerced = TypeCoerce[WithUnsupportedUnion].new.from({union: 'a'})
234
+ expect(coerced.union).to eq('a')
235
+
236
+ coerced = TypeCoerce[WithUnsupportedUnion].new.from({union: 2})
237
+ expect(coerced.union).to eq(2)
238
+
239
+ expect do
240
+ TypeCoerce[WithUnsupportedUnion].new.from({union: nil})
241
+ end.to raise_error(TypeError)
242
+ end
243
+ end
244
+ end
245
+
246
+ context 'when dealing with arrays' do
190
247
  it 'coreces correctly' do
191
248
  expect(TypeCoerce[T::Array[Integer]].new.from(nil)).to eql []
192
249
  expect(TypeCoerce[T::Array[Integer]].new.from('')).to eql []
@@ -267,18 +324,18 @@ describe TypeCoerce do
267
324
 
268
325
  context 'when dealing with enums' do
269
326
  it 'coerces a serialized enum correctly' do
270
- coerced = TypeCoerce[WithEnum].new.from(myenum: "test")
327
+ coerced = TypeCoerce[WithEnum].new.from({myenum: "test"})
271
328
  expect(coerced.myenum).to eq(TestEnum::Test)
272
329
  end
273
330
 
274
331
  it 'handles a real enum correctly' do
275
- coerced = TypeCoerce[WithEnum].new.from(myenum: TestEnum::Test)
332
+ coerced = TypeCoerce[WithEnum].new.from({myenum: TestEnum::Test})
276
333
  expect(coerced.myenum).to eq(TestEnum::Test)
277
334
  end
278
335
 
279
336
  it 'handles bad enum' do
280
337
  expect {
281
- TypeCoerce[WithEnum].new.from(myenum: "bad_key")
338
+ TypeCoerce[WithEnum].new.from({myenum: "bad_key"})
282
339
  }.to raise_error(TypeCoerce::CoercionError)
283
340
  end
284
341
  end
@@ -289,4 +346,68 @@ describe TypeCoerce do
289
346
  obj = CustomType.new(1)
290
347
  expect(TypeCoerce[T::Hash[String, T.untyped]].new.from({a: obj})).to eq({'a' => obj})
291
348
  end
349
+
350
+ it 'works with T::Types::FixedArray' do
351
+ type = T.type_alias { [Integer, String, Integer] }
352
+ coerced = TypeCoerce[type].new.from(['1', 2, '3'])
353
+ expect(coerced).to eql([1, '2', 3])
354
+
355
+ coerced = TypeCoerce[WithFixedArray].new.from({ arr: ['1', 2, '3'] })
356
+ expect(coerced.arr).to eql([1, '2', 3])
357
+ end
358
+
359
+ context 'when dealing with T.class_of' do
360
+ it 'keeps the value as-is' do
361
+ string_class_type = T.class_of(String)
362
+ expect(TypeCoerce[string_class_type].new.from(String)).to eql(String)
363
+ expect(TypeCoerce[string_class_type].new.from(CustomString)).to eql(CustomString)
364
+ expect do
365
+ TypeCoerce[string_class_type].new.from(Integer)
366
+ end.to raise_error(TypeError)
367
+ expect do
368
+ TypeCoerce[string_class_type].new.from('a')
369
+ end.to raise_error(TypeError)
370
+ end
371
+ end
372
+
373
+ context 'when dealing with unknown types' do
374
+ context 'raise_coercion_error is enabled' do
375
+ it 'raises error' do
376
+ expect do
377
+ TypeCoerce['a'].new.from('a', raise_coercion_error: true)
378
+ end.to raise_error(TypeCoerce::CoercionError)
379
+ end
380
+ end
381
+ context 'raise_coercion_error is disabled' do
382
+ it 'keeps the value as-is (and let Sorbet raise error)' do
383
+ expect do
384
+ TypeCoerce['a'].new.from('a', raise_coercion_error: false)
385
+ end.to raise_error(/must be an T::Types::Base/i)
386
+ end
387
+ end
388
+ end
389
+
390
+ context 'when dealing with coercing empty strings' do
391
+ context 'when flag is set' do
392
+ it 'coerces empty strings to nil from a simple type' do
393
+ expect(TypeCoerce[T.nilable(String)].new.from('', coerce_empty_to_nil: true)).to be_nil
394
+ end
395
+
396
+ it 'coerces empty strings to nil from a struct' do
397
+ coerced = TypeCoerce[WithNilableString].new.from({myvalue: ''}, coerce_empty_to_nil: true)
398
+ expect(coerced.myvalue).to eql(nil)
399
+ end
400
+ end
401
+
402
+ context 'when flag is not set' do
403
+ it 'coerces empty strings to nil from a simple type' do
404
+ expect(TypeCoerce[T.nilable(String)].new.from('', coerce_empty_to_nil: false)).to eql('')
405
+ end
406
+
407
+ it 'coerces empty strings to nil from a struct' do
408
+ coerced = TypeCoerce[WithNilableString].new.from({myvalue: ''}, coerce_empty_to_nil: false)
409
+ expect(coerced.myvalue).to eql('')
410
+ end
411
+ end
412
+ end
292
413
  end
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: false
2
2
  require 'sorbet-coerce'
3
3
 
4
4
  T.assert_type!(TypeCoerce[Integer].new.from('1'), Integer)
data/spec/spec_helper.rb CHANGED
@@ -1,13 +1,10 @@
1
1
  # typed: strict
2
- require 'byebug'
3
- require 'simplecov'
2
+ require "byebug"
3
+ require "simplecov"
4
+ require "simplecov-cobertura"
4
5
 
5
6
  SimpleCov.start
6
-
7
- if ENV['CI'] == 'true'
8
- require 'codecov'
9
- SimpleCov.formatter = SimpleCov::Formatter::Codecov
10
- end
7
+ SimpleCov.formatter = SimpleCov::Formatter::CoberturaFormatter
11
8
 
12
9
  RSpec.configure do |config|
13
10
  config.expect_with(:rspec) { |c| c.syntax = :expect }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sorbet-coerce
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chan Zuckerberg Initiative
@@ -76,40 +76,40 @@ dependencies:
76
76
  name: rspec
77
77
  requirement: !ruby/object:Gem::Requirement
78
78
  requirements:
79
- - - ">="
79
+ - - "~>"
80
80
  - !ruby/object:Gem::Version
81
81
  version: '3.8'
82
- - - "~>"
82
+ - - ">="
83
83
  - !ruby/object:Gem::Version
84
84
  version: '3.8'
85
85
  type: :development
86
86
  prerelease: false
87
87
  version_requirements: !ruby/object:Gem::Requirement
88
88
  requirements:
89
- - - ">="
89
+ - - "~>"
90
90
  - !ruby/object:Gem::Version
91
91
  version: '3.8'
92
- - - "~>"
92
+ - - ">="
93
93
  - !ruby/object:Gem::Version
94
94
  version: '3.8'
95
95
  - !ruby/object:Gem::Dependency
96
96
  name: byebug
97
97
  requirement: !ruby/object:Gem::Requirement
98
98
  requirements:
99
- - - ">="
99
+ - - "~>"
100
100
  - !ruby/object:Gem::Version
101
101
  version: 11.0.1
102
- - - "~>"
102
+ - - ">="
103
103
  - !ruby/object:Gem::Version
104
104
  version: 11.0.1
105
105
  type: :development
106
106
  prerelease: false
107
107
  version_requirements: !ruby/object:Gem::Requirement
108
108
  requirements:
109
- - - ">="
109
+ - - "~>"
110
110
  - !ruby/object:Gem::Version
111
111
  version: 11.0.1
112
- - - "~>"
112
+ - - ">="
113
113
  - !ruby/object:Gem::Version
114
114
  version: 11.0.1
115
115
  description:
@@ -139,14 +139,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
139
139
  requirements:
140
140
  - - ">="
141
141
  - !ruby/object:Gem::Version
142
- version: 2.4.0
142
+ version: 2.7.0
143
143
  required_rubygems_version: !ruby/object:Gem::Requirement
144
144
  requirements:
145
145
  - - ">="
146
146
  - !ruby/object:Gem::Version
147
147
  version: '0'
148
148
  requirements: []
149
- rubygems_version: 3.0.8
149
+ rubygems_version: 3.1.6
150
150
  signing_key:
151
151
  specification_version: 4
152
152
  summary: A type coercion lib works with Sorbet's static type checker and type definitions;