sorbet-schema 0.9.2 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4546ee5b80f3b538d7c3f9813501f066ef1f033693f5328f8789f26f9d7537dc
4
- data.tar.gz: 3c658049c62dcd8ab506bc3cc72fa926731fd8a712c1436f0277339edc88d290
3
+ metadata.gz: d6e96bccc5924a52824327d2d5bc3b8e79b887ac31d13b292581c347c1d43480
4
+ data.tar.gz: 36896e42540d07c714b8075c7c2b89b4d47c5d09d1e098e83ecf050f6c68fa50
5
5
  SHA512:
6
- metadata.gz: a2f8a6bec49a48abd11c0d6ee1eac7eb9d2a85965a02633635b9361d82bad46bedb6c02d213cf226f6cb644e803c575ded85e3eaf6c06cbe9ae34068e7dd1985
7
- data.tar.gz: db1a6c0bf48b2fd6628b77dd324e06b42d9cb7e8e222b54523e0beeb7e10395b5a86d2a3fd7b3afe36453f2b8386f3bde758cbd9893aefee9e8d1fca8490f533
6
+ metadata.gz: c0a12cf7754d2e13eaebb5a0b042a2abea848dc79aeda84b2887f0c949b523c2aa8a1b3a842562ee053c15dcb3b5a13c6caf1e760bbf9c7f297ea39285c5e1e3
7
+ data.tar.gz: b72bc80795f41444f06b2aa9bc7186f3669993c95cb7e3e500e021fc2c053c2ac5088d2e3a0efae32aa4e1bd0d35c138602de08fba9f31ded554b43ae7aefd4d
data/CHANGELOG.md CHANGED
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.9.3](https://github.com/maxveldink/sorbet-schema/compare/v0.9.2...v0.9.3) (2025-12-06)
8
+
9
+
10
+ ### Performance Improvements
11
+
12
+ * add deserialization benchmark ([#135](https://github.com/maxveldink/sorbet-schema/issues/135)) ([21d23bb](https://github.com/maxveldink/sorbet-schema/commit/21d23bb51c40ee199cb79a54f06184bb15634909))
13
+ * add serialization benchmarks, refactor to helpers ([#137](https://github.com/maxveldink/sorbet-schema/issues/137)) ([dc43409](https://github.com/maxveldink/sorbet-schema/commit/dc434094511f26ddc37f853295b2f07586826f00))
14
+ * Implement caching for serializers and coercers to improve performance ([#136](https://github.com/maxveldink/sorbet-schema/issues/136)) ([d980292](https://github.com/maxveldink/sorbet-schema/commit/d980292b64630c9ee93f886b10b3b9334b694879))
15
+ * Improve coercer performance by using class methods ([#134](https://github.com/maxveldink/sorbet-schema/issues/134)) ([0336299](https://github.com/maxveldink/sorbet-schema/commit/033629914c7ac0e19d21a725336ff7b9dbc8fc90))
16
+
7
17
  ## [0.9.2](https://github.com/maxveldink/sorbet-schema/compare/v0.9.1...v0.9.2) (2024-09-04)
8
18
 
9
19
 
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ source "https://rubygems.org"
6
6
  gemspec
7
7
 
8
8
  group :development do
9
+ gem "benchmark-ips"
9
10
  gem "rake"
10
11
  gem "standard"
11
12
  gem "standard-performance"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sorbet-schema (0.9.2)
4
+ sorbet-schema (0.9.3)
5
5
  sorbet-result (~> 1.1)
6
6
  sorbet-runtime (~> 0.5)
7
7
  sorbet-struct-comparable (~> 1.3)
@@ -12,6 +12,7 @@ GEM
12
12
  specs:
13
13
  ansi (1.5.0)
14
14
  ast (2.4.2)
15
+ benchmark-ips (2.14.0)
15
16
  bigdecimal (3.1.8)
16
17
  builder (3.3.0)
17
18
  debug (1.9.2)
@@ -127,9 +128,11 @@ GEM
127
128
  PLATFORMS
128
129
  arm64-darwin-22
129
130
  arm64-darwin-23
131
+ arm64-darwin-25
130
132
  x86_64-linux
131
133
 
132
134
  DEPENDENCIES
135
+ benchmark-ips
133
136
  bigdecimal
134
137
  debug
135
138
  minitest
@@ -0,0 +1,239 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
5
+
6
+ require "json"
7
+ require "sorbet-schema"
8
+
9
+ module BenchmarkHelpers
10
+ extend T::Sig
11
+
12
+ class Rank < T::Enum
13
+ enums do
14
+ Ace = new("Ace")
15
+ King = new("King")
16
+ Queen = new("Queen")
17
+ Jack = new("Jack")
18
+ Ten = new("Ten")
19
+ Nine = new("Nine")
20
+ Eight = new("Eight")
21
+ Seven = new("Seven")
22
+ Six = new("Six")
23
+ Five = new("Five")
24
+ Four = new("Four")
25
+ Three = new("Three")
26
+ Two = new("Two")
27
+ end
28
+ end
29
+
30
+ class Suit < T::Enum
31
+ enums do
32
+ Spades = new("Spades")
33
+ Hearts = new("Hearts")
34
+ Clubs = new("Clubs")
35
+ Diamonds = new("Diamonds")
36
+ end
37
+ end
38
+
39
+ # Define some structs for the benchmark
40
+ class Card < T::Struct
41
+ const :rank, Rank
42
+ const :suit, Suit
43
+ end
44
+
45
+ class Player < T::Struct
46
+ const :name, String
47
+ const :hand, T::Array[Card]
48
+ end
49
+
50
+ class Round < T::Struct
51
+ const :number, Integer
52
+ const :winner, T.nilable(Player)
53
+ end
54
+
55
+ class Game < T::Struct
56
+ const :name, String
57
+ const :players, T::Array[Player]
58
+ const :rounds, T::Array[Round]
59
+ const :deck_size, Integer
60
+ const :shuffled, T::Boolean
61
+ end
62
+
63
+ # Define a struct with MANY fields of the same types that require coercion
64
+ # This will stress-test coercer caching by reusing the same coercers many times
65
+ class Humongous < T::Struct
66
+ const :field_1, Integer
67
+ const :field_2, Integer
68
+ const :field_3, Integer
69
+ const :field_4, Integer
70
+ const :field_5, Integer
71
+ const :field_6, Integer
72
+ const :field_7, Integer
73
+ const :field_8, Integer
74
+ const :field_9, Integer
75
+ const :field_10, Integer
76
+ const :field_11, Integer
77
+ const :field_12, Integer
78
+ const :field_13, Integer
79
+ const :field_14, Integer
80
+ const :field_15, Integer
81
+ const :field_16, Integer
82
+ const :field_17, Integer
83
+ const :field_18, Integer
84
+ const :field_19, Integer
85
+ const :field_20, Integer
86
+ const :field_21, Integer
87
+ const :field_22, Integer
88
+ const :field_23, Integer
89
+ const :field_24, Integer
90
+ const :field_25, Integer
91
+ const :field_26, Integer
92
+ const :field_27, Integer
93
+ const :field_28, Integer
94
+ const :field_29, Integer
95
+ const :field_30, Integer
96
+ const :field_31, Integer
97
+ const :field_32, Integer
98
+ const :field_33, Integer
99
+ const :field_34, Integer
100
+ const :field_35, Integer
101
+ const :field_36, Integer
102
+ const :field_37, Integer
103
+ const :field_38, Integer
104
+ const :field_39, Integer
105
+ const :field_40, Integer
106
+ const :field_41, Integer
107
+ const :field_42, Integer
108
+ const :field_43, Integer
109
+ const :field_44, Integer
110
+ const :field_45, Integer
111
+ const :field_46, Integer
112
+ const :field_47, Integer
113
+ const :field_48, Integer
114
+ const :field_49, Integer
115
+ const :field_50, Integer
116
+
117
+ const :float_1, Float
118
+ const :float_2, Float
119
+ const :float_3, Float
120
+ const :float_4, Float
121
+ const :float_5, Float
122
+ const :float_6, Float
123
+ const :float_7, Float
124
+ const :float_8, Float
125
+ const :float_9, Float
126
+ const :float_10, Float
127
+ const :float_11, Float
128
+ const :float_12, Float
129
+ const :float_13, Float
130
+ const :float_14, Float
131
+ const :float_15, Float
132
+ const :float_16, Float
133
+ const :float_17, Float
134
+ const :float_18, Float
135
+ const :float_19, Float
136
+ const :float_20, Float
137
+
138
+ const :bool_1, T::Boolean
139
+ const :bool_2, T::Boolean
140
+ const :bool_3, T::Boolean
141
+ const :bool_4, T::Boolean
142
+ const :bool_5, T::Boolean
143
+ const :bool_6, T::Boolean
144
+ const :bool_7, T::Boolean
145
+ const :bool_8, T::Boolean
146
+ const :bool_9, T::Boolean
147
+ const :bool_10, T::Boolean
148
+ const :bool_11, T::Boolean
149
+ const :bool_12, T::Boolean
150
+ const :bool_13, T::Boolean
151
+ const :bool_14, T::Boolean
152
+ const :bool_15, T::Boolean
153
+ const :bool_16, T::Boolean
154
+ const :bool_17, T::Boolean
155
+ const :bool_18, T::Boolean
156
+ const :bool_19, T::Boolean
157
+ const :bool_20, T::Boolean
158
+
159
+ const :sym_1, Symbol
160
+ const :sym_2, Symbol
161
+ const :sym_3, Symbol
162
+ const :sym_4, Symbol
163
+ const :sym_5, Symbol
164
+ const :sym_6, Symbol
165
+ const :sym_7, Symbol
166
+ const :sym_8, Symbol
167
+ const :sym_9, Symbol
168
+ const :sym_10, Symbol
169
+ const :sym_11, Symbol
170
+ const :sym_12, Symbol
171
+ const :sym_13, Symbol
172
+ const :sym_14, Symbol
173
+ const :sym_15, Symbol
174
+ const :sym_16, Symbol
175
+ const :sym_17, Symbol
176
+ const :sym_18, Symbol
177
+ const :sym_19, Symbol
178
+ const :sym_20, Symbol
179
+ end
180
+
181
+ sig { returns(T::Hash[Symbol, T.untyped]) }
182
+ def self.game_data
183
+ {
184
+ name: "Poker",
185
+ players: Array.new(10) do |i|
186
+ {
187
+ name: "Player #{i}",
188
+ hand: [
189
+ {rank: "Ace", suit: "Spades"},
190
+ {rank: "King", suit: "Spades"}
191
+ ]
192
+ }
193
+ end,
194
+ rounds: Array.new(5) do |i|
195
+ {
196
+ number: i + 1,
197
+ winner: {
198
+ name: "Player #{i % 2}",
199
+ hand: [
200
+ {rank: "Queen", suit: "Hearts"},
201
+ {rank: "Jack", suit: "Hearts"}
202
+ ]
203
+ }
204
+ }
205
+ end,
206
+ deck_size: 52,
207
+ shuffled: true
208
+ }
209
+ end
210
+
211
+ sig { returns(T::Hash[Symbol, T.untyped]) }
212
+ def self.humongous_data
213
+ {
214
+ field_1: "1", field_2: "2", field_3: "3", field_4: "4", field_5: "5",
215
+ field_6: "6", field_7: "7", field_8: "8", field_9: "9", field_10: "10",
216
+ field_11: "11", field_12: "12", field_13: "13", field_14: "14", field_15: "15",
217
+ field_16: "16", field_17: "17", field_18: "18", field_19: "19", field_20: "20",
218
+ field_21: "21", field_22: "22", field_23: "23", field_24: "24", field_25: "25",
219
+ field_26: "26", field_27: "27", field_28: "28", field_29: "29", field_30: "30",
220
+ field_31: "31", field_32: "32", field_33: "33", field_34: "34", field_35: "35",
221
+ field_36: "36", field_37: "37", field_38: "38", field_39: "39", field_40: "40",
222
+ field_41: "41", field_42: "42", field_43: "43", field_44: "44", field_45: "45",
223
+ field_46: "46", field_47: "47", field_48: "48", field_49: "49", field_50: "50",
224
+ float_1: "1.1", float_2: "2.2", float_3: "3.3", float_4: "4.4", float_5: "5.5",
225
+ float_6: "6.6", float_7: "7.7", float_8: "8.8", float_9: "9.9", float_10: "10.1",
226
+ float_11: "11.1", float_12: "12.2", float_13: "13.3", float_14: "14.4", float_15: "15.5",
227
+ float_16: "16.6", float_17: "17.7", float_18: "18.8", float_19: "19.9", float_20: "20.1",
228
+ bool_1: "true", bool_2: "false", bool_3: "true", bool_4: "false", bool_5: "true",
229
+ bool_6: "false", bool_7: "true", bool_8: "false", bool_9: "true", bool_10: "false",
230
+ bool_11: "true", bool_12: "false", bool_13: "true", bool_14: "false", bool_15: "true",
231
+ bool_16: "false", bool_17: "true", bool_18: "false", bool_19: "true", bool_20: "false",
232
+ sym_1: "sym1", sym_2: "sym2", sym_3: "sym3", sym_4: "sym4", sym_5: "sym5",
233
+ sym_6: "sym6", sym_7: "sym7", sym_8: "sym8", sym_9: "sym9", sym_10: "sym10",
234
+ sym_11: "sym11", sym_12: "sym12", sym_13: "sym13", sym_14: "sym14", sym_15: "sym15",
235
+ sym_16: "sym16", sym_17: "sym17", sym_18: "sym18", sym_19: "sym19", sym_20: "sym20"
236
+ }
237
+ end
238
+ end
239
+
@@ -0,0 +1,23 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
5
+
6
+ require "benchmark/ips"
7
+ require_relative "helpers"
8
+
9
+ humongous_schema = Typed::Schema.from_struct(BenchmarkHelpers::Humongous)
10
+ humongous_data = BenchmarkHelpers.humongous_data
11
+ humongous_json = JSON.generate(humongous_data)
12
+
13
+ Benchmark.ips do |x|
14
+ x.report("hash deserialization with many fields") do
15
+ humongous_schema.from_hash(humongous_data)
16
+ end
17
+
18
+ x.report("json deserialization with many fields") do
19
+ humongous_schema.from_json(humongous_json)
20
+ end
21
+
22
+ x.compare!
23
+ end
@@ -0,0 +1,27 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
5
+
6
+ require "benchmark/ips"
7
+ require_relative "helpers"
8
+
9
+ humongous_schema = Typed::Schema.from_struct(BenchmarkHelpers::Humongous)
10
+ humongous_data = BenchmarkHelpers.humongous_data
11
+ humongous_struct = humongous_schema.from_hash(humongous_data).payload
12
+
13
+ hash_serializer = Typed::HashSerializer.new(schema: humongous_schema)
14
+ json_serializer = Typed::JSONSerializer.new(schema: humongous_schema)
15
+
16
+ Benchmark.ips do |x|
17
+ x.report("hash serialization with many fields") do
18
+ hash_serializer.serialize(humongous_struct)
19
+ end
20
+
21
+ x.report("json serialization with many fields") do
22
+ json_serializer.serialize(humongous_struct)
23
+ end
24
+
25
+ x.compare!
26
+ end
27
+
@@ -0,0 +1,23 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
5
+
6
+ require "benchmark/ips"
7
+ require_relative "helpers"
8
+
9
+ game_schema = Typed::Schema.from_struct(BenchmarkHelpers::Game)
10
+ game_data = BenchmarkHelpers.game_data
11
+ game_json = JSON.generate(game_data)
12
+
13
+ Benchmark.ips do |x|
14
+ x.report("simple hash deserialization") do
15
+ game_schema.from_hash(game_data)
16
+ end
17
+
18
+ x.report("simple json deserialization") do
19
+ game_schema.from_json(game_json)
20
+ end
21
+
22
+ x.compare!
23
+ end
@@ -0,0 +1,27 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
5
+
6
+ require "benchmark/ips"
7
+ require_relative "helpers"
8
+
9
+ game_schema = Typed::Schema.from_struct(BenchmarkHelpers::Game)
10
+ game_data = BenchmarkHelpers.game_data
11
+ game_struct = game_schema.from_hash(game_data).payload
12
+
13
+ hash_serializer = Typed::HashSerializer.new(schema: game_schema)
14
+ json_serializer = Typed::JSONSerializer.new(schema: game_schema)
15
+
16
+ Benchmark.ips do |x|
17
+ x.report("simple hash serialization") do
18
+ hash_serializer.serialize(game_struct)
19
+ end
20
+
21
+ x.report("simple json serialization") do
22
+ json_serializer.serialize(game_struct)
23
+ end
24
+
25
+ x.compare!
26
+ end
27
+
@@ -1,5 +1,5 @@
1
1
  # typed: strict
2
2
 
3
3
  module SorbetSchema
4
- VERSION = "0.9.2"
4
+ VERSION = "0.9.3"
5
5
  end
@@ -8,13 +8,13 @@ module Typed
8
8
  Target = type_member { {fixed: T::Boolean} }
9
9
 
10
10
  sig { override.params(type: T::Types::Base).returns(T::Boolean) }
11
- def used_for_type?(type)
11
+ def self.used_for_type?(type)
12
12
  type == T::Utils.coerce(T::Boolean)
13
13
  end
14
14
 
15
15
  sig { override.params(type: T::Types::Base, value: Value).returns(Result[Target, CoercionError]) }
16
16
  def coerce(type:, value:)
17
- return Failure.new(CoercionError.new("Type must be a T::Boolean.")) unless used_for_type?(type)
17
+ return Failure.new(CoercionError.new("Type must be a T::Boolean.")) unless self.class.used_for_type?(type)
18
18
 
19
19
  if type.recursively_valid?(value)
20
20
  Success.new(value)
@@ -11,7 +11,7 @@ module Typed
11
11
  Target = type_member(:out)
12
12
 
13
13
  sig { abstract.params(type: T::Types::Base).returns(T::Boolean) }
14
- def used_for_type?(type)
14
+ def self.used_for_type?(type)
15
15
  end
16
16
 
17
17
  sig { abstract.params(type: T::Types::Base, value: Value).returns(Result[Target, CoercionError]) }
@@ -45,7 +45,7 @@ module Typed
45
45
 
46
46
  sig { params(type: T::Types::Base).returns(T.nilable(T.class_of(Coercer))) }
47
47
  def select_coercer_by(type:)
48
- @available.find { |coercer| coercer.new.used_for_type?(type) }
48
+ @available.find { |coercer| coercer.used_for_type?(type) }
49
49
  end
50
50
  end
51
51
  end
@@ -10,13 +10,13 @@ module Typed
10
10
  Target = type_member { {fixed: Date} }
11
11
 
12
12
  sig { override.params(type: T::Types::Base).returns(T::Boolean) }
13
- def used_for_type?(type)
13
+ def self.used_for_type?(type)
14
14
  T::Utils.coerce(type) == T::Utils.coerce(Date)
15
15
  end
16
16
 
17
17
  sig { override.params(type: T::Types::Base, value: Value).returns(Result[Target, CoercionError]) }
18
18
  def coerce(type:, value:)
19
- return Failure.new(CoercionError.new("Type must be a Date.")) unless used_for_type?(type)
19
+ return Failure.new(CoercionError.new("Type must be a Date.")) unless self.class.used_for_type?(type)
20
20
 
21
21
  return Success.new(value) if value.is_a?(Date)
22
22
 
@@ -10,13 +10,13 @@ module Typed
10
10
  Target = type_member { {fixed: DateTime} }
11
11
 
12
12
  sig { override.params(type: T::Types::Base).returns(T::Boolean) }
13
- def used_for_type?(type)
13
+ def self.used_for_type?(type)
14
14
  T::Utils.coerce(type) == T::Utils.coerce(DateTime)
15
15
  end
16
16
 
17
17
  sig { override.params(type: T::Types::Base, value: Value).returns(Result[Target, CoercionError]) }
18
18
  def coerce(type:, value:)
19
- return Failure.new(CoercionError.new("Type must be a DateTime.")) unless used_for_type?(type)
19
+ return Failure.new(CoercionError.new("Type must be a DateTime.")) unless self.class.used_for_type?(type)
20
20
 
21
21
  return Success.new(value) if value.is_a?(DateTime)
22
22
 
@@ -8,7 +8,7 @@ module Typed
8
8
  Target = type_member { {fixed: T::Enum} }
9
9
 
10
10
  sig { override.params(type: T::Types::Base).returns(T::Boolean) }
11
- def used_for_type?(type)
11
+ def self.used_for_type?(type)
12
12
  return false unless type.respond_to?(:raw_type)
13
13
 
14
14
  !!(T.cast(type, T::Types::Simple).raw_type < T::Enum)
@@ -16,7 +16,7 @@ module Typed
16
16
 
17
17
  sig { override.params(type: T::Types::Base, value: Value).returns(Result[Target, CoercionError]) }
18
18
  def coerce(type:, value:)
19
- return Failure.new(CoercionError.new("Field type must inherit from T::Enum for Enum coercion.")) unless used_for_type?(type)
19
+ return Failure.new(CoercionError.new("Field type must inherit from T::Enum for Enum coercion.")) unless self.class.used_for_type?(type)
20
20
 
21
21
  Success.new(T.cast(type, T::Types::Simple).raw_type.from_serialized(value))
22
22
  rescue KeyError => e
@@ -8,13 +8,13 @@ module Typed
8
8
  Target = type_member { {fixed: Float} }
9
9
 
10
10
  sig { override.params(type: T::Types::Base).returns(T::Boolean) }
11
- def used_for_type?(type)
11
+ def self.used_for_type?(type)
12
12
  T::Utils.coerce(type) == T::Utils.coerce(Float)
13
13
  end
14
14
 
15
15
  sig { override.params(type: T::Types::Base, value: Value).returns(Result[Target, CoercionError]) }
16
16
  def coerce(type:, value:)
17
- return Failure.new(CoercionError.new("Type must be a Float.")) unless used_for_type?(type)
17
+ return Failure.new(CoercionError.new("Type must be a Float.")) unless self.class.used_for_type?(type)
18
18
 
19
19
  Success.new(Float(value))
20
20
  rescue ArgumentError, TypeError
@@ -8,13 +8,13 @@ module Typed
8
8
  Target = type_member { {fixed: Integer} }
9
9
 
10
10
  sig { override.params(type: T::Types::Base).returns(T::Boolean) }
11
- def used_for_type?(type)
11
+ def self.used_for_type?(type)
12
12
  type == T::Utils.coerce(Integer)
13
13
  end
14
14
 
15
15
  sig { override.params(type: T::Types::Base, value: Value).returns(Result[Target, CoercionError]) }
16
16
  def coerce(type:, value:)
17
- return Failure.new(CoercionError.new("Type must be a Integer.")) unless used_for_type?(type)
17
+ return Failure.new(CoercionError.new("Type must be a Integer.")) unless self.class.used_for_type?(type)
18
18
 
19
19
  Success.new(Integer(value))
20
20
  rescue ArgumentError, TypeError
@@ -8,13 +8,13 @@ module Typed
8
8
  Target = type_member { {fixed: String} }
9
9
 
10
10
  sig { override.params(type: T::Types::Base).returns(T::Boolean) }
11
- def used_for_type?(type)
11
+ def self.used_for_type?(type)
12
12
  type == T::Utils.coerce(String)
13
13
  end
14
14
 
15
15
  sig { override.params(type: T::Types::Base, value: Value).returns(Result[Target, CoercionError]) }
16
16
  def coerce(type:, value:)
17
- return Failure.new(CoercionError.new("Type must be a String.")) unless used_for_type?(type)
17
+ return Failure.new(CoercionError.new("Type must be a String.")) unless self.class.used_for_type?(type)
18
18
 
19
19
  Success.new(String(value))
20
20
  end
@@ -8,7 +8,7 @@ module Typed
8
8
  Target = type_member { {fixed: T::Struct} }
9
9
 
10
10
  sig { override.params(type: T::Types::Base).returns(T::Boolean) }
11
- def used_for_type?(type)
11
+ def self.used_for_type?(type)
12
12
  return false unless type.respond_to?(:raw_type)
13
13
 
14
14
  !!(T.cast(type, T::Types::Simple).raw_type < T::Struct)
@@ -16,7 +16,7 @@ module Typed
16
16
 
17
17
  sig { override.params(type: T::Types::Base, value: Value).returns(Result[Target, CoercionError]) }
18
18
  def coerce(type:, value:)
19
- return Failure.new(CoercionError.new("Field type must inherit from T::Struct for Struct coercion.")) unless used_for_type?(type)
19
+ return Failure.new(CoercionError.new("Field type must inherit from T::Struct for Struct coercion.")) unless self.class.used_for_type?(type)
20
20
  return Success.new(value) if type.recursively_valid?(value)
21
21
 
22
22
  return Failure.new(CoercionError.new("Value of type '#{value.class}' cannot be coerced to #{type} Struct.")) unless value.is_a?(Hash)
@@ -8,13 +8,13 @@ module Typed
8
8
  Target = type_member { {fixed: Symbol} }
9
9
 
10
10
  sig { override.params(type: T::Types::Base).returns(T::Boolean) }
11
- def used_for_type?(type)
11
+ def self.used_for_type?(type)
12
12
  type == T::Utils.coerce(Symbol)
13
13
  end
14
14
 
15
15
  sig { override.params(type: T::Types::Base, value: Value).returns(Result[Target, CoercionError]) }
16
16
  def coerce(type:, value:)
17
- return Failure.new(CoercionError.new("Type must be a Symbol.")) unless used_for_type?(type)
17
+ return Failure.new(CoercionError.new("Type must be a Symbol.")) unless self.class.used_for_type?(type)
18
18
 
19
19
  if value.respond_to?(:to_sym)
20
20
  Success.new(value.to_sym)
@@ -8,13 +8,13 @@ module Typed
8
8
  Target = type_member { {fixed: T::Array[T.untyped]} }
9
9
 
10
10
  sig { override.params(type: T::Types::Base).returns(T::Boolean) }
11
- def used_for_type?(type)
11
+ def self.used_for_type?(type)
12
12
  type.is_a?(T::Types::TypedArray)
13
13
  end
14
14
 
15
15
  sig { override.params(type: T::Types::Base, value: Value).returns(Result[Target, CoercionError]) }
16
16
  def coerce(type:, value:)
17
- return Failure.new(CoercionError.new("Field type must be a T::Array.")) unless used_for_type?(type)
17
+ return Failure.new(CoercionError.new("Field type must be a T::Array.")) unless self.class.used_for_type?(type)
18
18
  return Failure.new(CoercionError.new("Value must be an Array.")) unless value.is_a?(Array)
19
19
 
20
20
  return Success.new(value) if type.recursively_valid?(value)
@@ -8,13 +8,13 @@ module Typed
8
8
  Target = type_member { {fixed: T::Hash[T.untyped, T.untyped]} }
9
9
 
10
10
  sig { override.params(type: T::Types::Base).returns(T::Boolean) }
11
- def used_for_type?(type)
11
+ def self.used_for_type?(type)
12
12
  type.is_a?(T::Types::TypedHash)
13
13
  end
14
14
 
15
15
  sig { override.params(type: T::Types::Base, value: Value).returns(Result[Target, CoercionError]) }
16
16
  def coerce(type:, value:)
17
- return Failure.new(CoercionError.new("Field type must be a T::Hash.")) unless used_for_type?(type)
17
+ return Failure.new(CoercionError.new("Field type must be a T::Hash.")) unless self.class.used_for_type?(type)
18
18
  return Failure.new(CoercionError.new("Value must be a Hash.")) unless value.is_a?(Hash)
19
19
 
20
20
  return Success.new(value) if type.recursively_valid?(value)
data/lib/typed/schema.rb CHANGED
@@ -20,12 +20,12 @@ module Typed
20
20
 
21
21
  sig { params(hash: Typed::HashSerializer::InputHash).returns(Typed::Serializer::DeserializeResult) }
22
22
  def from_hash(hash)
23
- Typed::HashSerializer.new(schema: self).deserialize(hash)
23
+ hash_serializer.deserialize(hash)
24
24
  end
25
25
 
26
26
  sig { params(json: String).returns(Typed::Serializer::DeserializeResult) }
27
27
  def from_json(json)
28
- Typed::JSONSerializer.new(schema: self).deserialize(json)
28
+ json_serializer.deserialize(json)
29
29
  end
30
30
 
31
31
  sig { params(field_name: Symbol, serializer: Field::InlineSerializer).returns(Schema) }
@@ -41,5 +41,19 @@ module Typed
41
41
  end
42
42
  )
43
43
  end
44
+
45
+ private
46
+
47
+ sig { returns(Typed::HashSerializer) }
48
+ def hash_serializer
49
+ @hash_serializer = T.let(@hash_serializer, T.nilable(Typed::HashSerializer))
50
+ @hash_serializer ||= Typed::HashSerializer.new(schema: self)
51
+ end
52
+
53
+ sig { returns(Typed::JSONSerializer) }
54
+ def json_serializer
55
+ @json_serializer = T.let(@json_serializer, T.nilable(Typed::JSONSerializer))
56
+ @json_serializer ||= Typed::JSONSerializer.new(schema: self)
57
+ end
44
58
  end
45
59
  end