sorbet-schema 0.3.0 → 0.4.0
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 +4 -4
- data/CHANGELOG.md +27 -0
- data/Gemfile.lock +1 -1
- data/lib/sorbet-schema/hash_transformer.rb +27 -2
- data/lib/sorbet-schema/version.rb +1 -1
- data/lib/typed/coercion/boolean_coercer.rb +31 -0
- data/lib/typed/coercion/coercer.rb +3 -3
- data/lib/typed/coercion/coercer_registry.rb +13 -2
- data/lib/typed/coercion/enum_coercer.rb +25 -0
- data/lib/typed/coercion/float_coercer.rb +4 -4
- data/lib/typed/coercion/integer_coercer.rb +3 -3
- data/lib/typed/coercion/string_coercer.rb +3 -3
- data/lib/typed/coercion/struct_coercer.rb +7 -7
- data/lib/typed/coercion/typed_array_coercer.rb +34 -0
- data/lib/typed/coercion.rb +4 -4
- data/lib/typed/deserialize_error.rb +1 -1
- data/lib/typed/field.rb +10 -1
- data/lib/typed/hash_serializer.rb +20 -6
- data/lib/typed/json_serializer.rb +4 -2
- data/lib/typed/schema.rb +10 -0
- data/lib/typed/serialization_error.rb +17 -0
- data/lib/typed/serialize_error.rb +6 -0
- data/lib/typed/serializer.rb +5 -7
- data/lib/typed/validations/field_type_validator.rb +1 -1
- data/lib/typed/validations/type_mismatch_error.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9af17198e776435ac97075ce33b365c34fd539aa7998ca9f60f9eef1a57ddd95
|
4
|
+
data.tar.gz: 8afbc08bc51a6cdc849803273af0c44e9f8a86e04991d9cd70767e565b3c68ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 260e1ee1603ef6ae6ddd5cc20544a874a097b65f95736e42dc97f489b979df5a6a41d48124d2a5f5c2643e9f0989de0e67ed7a7f787adabf39fd12168bc05b65
|
7
|
+
data.tar.gz: 94836f2ea97f2c4ac158562f2a6b9ffdd05d4ad2a0f34423e77d42b989c894caf08e6fdc28693491d8903d826aa3da860a4d85ceb14ba37b0a448542a41b51e7
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,33 @@ 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.4.0](https://github.com/maxveldink/sorbet-schema/compare/v0.3.0...v0.4.0) (2024-03-14)
|
8
|
+
|
9
|
+
|
10
|
+
### ⚠ BREAKING CHANGES
|
11
|
+
|
12
|
+
* Have coercers take in a type instead of the full field
|
13
|
+
* Update Field's types to a support Sorbets T::Types::Base classes
|
14
|
+
* Changed serialize return value to a Result
|
15
|
+
* adds SerializationError ancestor
|
16
|
+
|
17
|
+
### Features
|
18
|
+
|
19
|
+
* Add BooleanCoercer ([acd220f](https://github.com/maxveldink/sorbet-schema/commit/acd220f55fe0ebc823de6cc43776a1f510a4acd0))
|
20
|
+
* Add EnumCoercer ([5c0e2b5](https://github.com/maxveldink/sorbet-schema/commit/5c0e2b51990dfb72218129b0935f881622324194))
|
21
|
+
* Add from_hash and from_json helpers to Schemas ([#44](https://github.com/maxveldink/sorbet-schema/issues/44)) ([55c2da7](https://github.com/maxveldink/sorbet-schema/commit/55c2da77b7c2636339c684560bf15c51ff3ff4b1))
|
22
|
+
* Add idempotency to Struct coercer ([3a42957](https://github.com/maxveldink/sorbet-schema/commit/3a42957836afee55b074fc5c8a41eac6d31ada70))
|
23
|
+
* Add option to serialize values to HashSerializer ([710d365](https://github.com/maxveldink/sorbet-schema/commit/710d365d477bd6c6bff20929fcdcc1d1cc95bdb3))
|
24
|
+
* Adds TypedArray coercer ([795ddd9](https://github.com/maxveldink/sorbet-schema/commit/795ddd97f05502b154d4cb165878c1f78935cb3c))
|
25
|
+
|
26
|
+
|
27
|
+
### Code Refactoring
|
28
|
+
|
29
|
+
* adds SerializationError ancestor ([f8ea753](https://github.com/maxveldink/sorbet-schema/commit/f8ea75304613cbab17f698006698a2524a44b538))
|
30
|
+
* Changed serialize return value to a Result ([948c678](https://github.com/maxveldink/sorbet-schema/commit/948c67815dab19793dd2f321a90797d123740e0e))
|
31
|
+
* Have coercers take in a type instead of the full field ([c06169e](https://github.com/maxveldink/sorbet-schema/commit/c06169e0fa1cf7c8f645ecabef19cc2f5facffe4))
|
32
|
+
* Update Field's types to a support Sorbets T::Types::Base classes ([9ef1cd5](https://github.com/maxveldink/sorbet-schema/commit/9ef1cd5caf09396d8dae6a4db1feeb4f88dd25e9))
|
33
|
+
|
7
34
|
## [0.3.0](https://github.com/maxveldink/sorbet-schema/compare/v0.2.2...v0.3.0) (2024-03-12)
|
8
35
|
|
9
36
|
|
data/Gemfile.lock
CHANGED
@@ -6,17 +6,42 @@
|
|
6
6
|
class HashTransformer
|
7
7
|
extend T::Sig
|
8
8
|
|
9
|
+
sig { params(should_serialize_values: T::Boolean).void }
|
10
|
+
def initialize(should_serialize_values: false)
|
11
|
+
@should_serialize_values = should_serialize_values
|
12
|
+
end
|
13
|
+
|
9
14
|
sig { params(hash: T::Hash[T.untyped, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
10
15
|
def deep_symbolize_keys(hash)
|
11
16
|
hash.each_with_object({}) do |(key, value), result|
|
12
|
-
result[key.to_sym] = value
|
17
|
+
result[key.to_sym] = transform_value(value, hash_transformation_method: :deep_symbolize_keys)
|
13
18
|
end
|
14
19
|
end
|
15
20
|
|
16
21
|
sig { params(hash: T::Hash[T.untyped, T.untyped]).returns(T::Hash[String, T.untyped]) }
|
17
22
|
def deep_stringify_keys(hash)
|
18
23
|
hash.each_with_object({}) do |(key, value), result|
|
19
|
-
result[key.to_s] = value
|
24
|
+
result[key.to_s] = transform_value(value, hash_transformation_method: :deep_stringify_keys)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
sig { returns(T::Boolean) }
|
31
|
+
attr_reader :should_serialize_values
|
32
|
+
|
33
|
+
sig { params(value: T.untyped, hash_transformation_method: Symbol).returns(T.untyped) }
|
34
|
+
def transform_value(value, hash_transformation_method:)
|
35
|
+
if value.is_a?(Hash)
|
36
|
+
send(hash_transformation_method, value)
|
37
|
+
elsif value.is_a?(Array)
|
38
|
+
value.map { |inner_val| transform_value(inner_val, hash_transformation_method: hash_transformation_method) }
|
39
|
+
elsif value.is_a?(T::Struct) && should_serialize_values
|
40
|
+
deep_symbolize_keys(value.serialize)
|
41
|
+
elsif value.respond_to?(:serialize) && should_serialize_values
|
42
|
+
value.serialize
|
43
|
+
else
|
44
|
+
value
|
20
45
|
end
|
21
46
|
end
|
22
47
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module Typed
|
4
|
+
module Coercion
|
5
|
+
class BooleanCoercer < Coercer
|
6
|
+
extend T::Generic
|
7
|
+
|
8
|
+
Target = type_member { {fixed: T::Boolean} }
|
9
|
+
|
10
|
+
sig { override.params(type: Field::Type).returns(T::Boolean) }
|
11
|
+
def used_for_type?(type)
|
12
|
+
type == T::Utils.coerce(T::Boolean)
|
13
|
+
end
|
14
|
+
|
15
|
+
sig { override.params(type: Field::Type, value: Value).returns(Result[Target, CoercionError]) }
|
16
|
+
def coerce(type:, value:)
|
17
|
+
if T.cast(type, T::Types::Base).recursively_valid?(value)
|
18
|
+
Success.new(value)
|
19
|
+
elsif value == "true"
|
20
|
+
Success.new(true)
|
21
|
+
elsif value == "false"
|
22
|
+
Success.new(false)
|
23
|
+
else
|
24
|
+
Failure.new(CoercionError.new)
|
25
|
+
end
|
26
|
+
rescue TypeError
|
27
|
+
Failure.new(CoercionError.new("Field type must be a T::Boolean."))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -10,12 +10,12 @@ module Typed
|
|
10
10
|
|
11
11
|
Target = type_member(:out)
|
12
12
|
|
13
|
-
sig { abstract.params(type:
|
13
|
+
sig { abstract.params(type: Field::Type).returns(T::Boolean) }
|
14
14
|
def used_for_type?(type)
|
15
15
|
end
|
16
16
|
|
17
|
-
sig { abstract.params(
|
18
|
-
def coerce(
|
17
|
+
sig { abstract.params(type: Field::Type, value: Value).returns(Result[Target, CoercionError]) }
|
18
|
+
def coerce(type:, value:)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
@@ -11,7 +11,18 @@ module Typed
|
|
11
11
|
|
12
12
|
Registry = T.type_alias { T::Array[T.class_of(Coercer)] }
|
13
13
|
|
14
|
-
DEFAULT_COERCERS = T.let(
|
14
|
+
DEFAULT_COERCERS = T.let(
|
15
|
+
[
|
16
|
+
StringCoercer,
|
17
|
+
BooleanCoercer,
|
18
|
+
IntegerCoercer,
|
19
|
+
FloatCoercer,
|
20
|
+
EnumCoercer,
|
21
|
+
StructCoercer,
|
22
|
+
TypedArrayCoercer
|
23
|
+
],
|
24
|
+
Registry
|
25
|
+
)
|
15
26
|
|
16
27
|
sig { void }
|
17
28
|
def initialize
|
@@ -28,7 +39,7 @@ module Typed
|
|
28
39
|
@available = DEFAULT_COERCERS.clone
|
29
40
|
end
|
30
41
|
|
31
|
-
sig { params(type:
|
42
|
+
sig { params(type: Field::Type).returns(T.nilable(T.class_of(Coercer))) }
|
32
43
|
def select_coercer_by(type:)
|
33
44
|
@available.find { |coercer| coercer.new.used_for_type?(type) }
|
34
45
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module Typed
|
4
|
+
module Coercion
|
5
|
+
class EnumCoercer < Coercer
|
6
|
+
extend T::Generic
|
7
|
+
|
8
|
+
Target = type_member { {fixed: T::Enum} }
|
9
|
+
|
10
|
+
sig { override.params(type: Field::Type).returns(T::Boolean) }
|
11
|
+
def used_for_type?(type)
|
12
|
+
type.is_a?(Class) && !!(type < T::Enum)
|
13
|
+
end
|
14
|
+
|
15
|
+
sig { override.params(type: Field::Type, value: Value).returns(Result[Target, CoercionError]) }
|
16
|
+
def coerce(type:, value:)
|
17
|
+
return Failure.new(CoercionError.new("Field type must inherit from T::Enum for Enum coercion.")) unless type.is_a?(Class) && !!(type < T::Enum)
|
18
|
+
|
19
|
+
Success.new(type.from_serialized(value))
|
20
|
+
rescue KeyError => e
|
21
|
+
Failure.new(CoercionError.new(e.message))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -7,13 +7,13 @@ module Typed
|
|
7
7
|
|
8
8
|
Target = type_member { {fixed: Float} }
|
9
9
|
|
10
|
-
sig { override.params(type:
|
10
|
+
sig { override.params(type: Field::Type).returns(T::Boolean) }
|
11
11
|
def used_for_type?(type)
|
12
|
-
type == Float
|
12
|
+
T::Utils.coerce(type) == T::Utils.coerce(Float)
|
13
13
|
end
|
14
14
|
|
15
|
-
sig { override.params(
|
16
|
-
def coerce(
|
15
|
+
sig { override.params(type: Field::Type, value: Value).returns(Result[Target, CoercionError]) }
|
16
|
+
def coerce(type:, value:)
|
17
17
|
Success.new(Float(value))
|
18
18
|
rescue ArgumentError, TypeError
|
19
19
|
Failure.new(CoercionError.new("'#{value}' cannot be coerced into Float."))
|
@@ -7,13 +7,13 @@ module Typed
|
|
7
7
|
|
8
8
|
Target = type_member { {fixed: Integer} }
|
9
9
|
|
10
|
-
sig { override.params(type:
|
10
|
+
sig { override.params(type: Field::Type).returns(T::Boolean) }
|
11
11
|
def used_for_type?(type)
|
12
12
|
type == Integer
|
13
13
|
end
|
14
14
|
|
15
|
-
sig { override.params(
|
16
|
-
def coerce(
|
15
|
+
sig { override.params(type: Field::Type, value: Value).returns(Result[Target, CoercionError]) }
|
16
|
+
def coerce(type:, value:)
|
17
17
|
Success.new(Integer(value))
|
18
18
|
rescue ArgumentError, TypeError
|
19
19
|
Failure.new(CoercionError.new("'#{value}' cannot be coerced into Integer."))
|
@@ -7,13 +7,13 @@ module Typed
|
|
7
7
|
|
8
8
|
Target = type_member { {fixed: String} }
|
9
9
|
|
10
|
-
sig { override.params(type:
|
10
|
+
sig { override.params(type: Field::Type).returns(T::Boolean) }
|
11
11
|
def used_for_type?(type)
|
12
12
|
type == String
|
13
13
|
end
|
14
14
|
|
15
|
-
sig { override.params(
|
16
|
-
def coerce(
|
15
|
+
sig { override.params(type: Field::Type, value: Value).returns(Result[Target, CoercionError]) }
|
16
|
+
def coerce(type:, value:)
|
17
17
|
Success.new(String(value))
|
18
18
|
end
|
19
19
|
end
|
@@ -7,17 +7,17 @@ module Typed
|
|
7
7
|
|
8
8
|
Target = type_member { {fixed: T::Struct} }
|
9
9
|
|
10
|
-
sig { override.params(type:
|
10
|
+
sig { override.params(type: Field::Type).returns(T::Boolean) }
|
11
11
|
def used_for_type?(type)
|
12
|
-
!!(type < T::Struct)
|
12
|
+
type.is_a?(Class) && !!(type < T::Struct)
|
13
13
|
end
|
14
14
|
|
15
|
-
sig { override.params(
|
16
|
-
def coerce(
|
17
|
-
type
|
15
|
+
sig { override.params(type: Field::Type, value: Value).returns(Result[Target, CoercionError]) }
|
16
|
+
def coerce(type:, value:)
|
17
|
+
return Failure.new(CoercionError.new("Field type must inherit from T::Struct for Struct coercion.")) unless type.is_a?(Class) && type < T::Struct
|
18
|
+
return Success.new(value) if value.instance_of?(type)
|
18
19
|
|
19
|
-
return Failure.new(CoercionError.new("
|
20
|
-
return Failure.new(CoercionError.new("Value must be a Hash for Struct coercion.")) unless value.is_a?(Hash)
|
20
|
+
return Failure.new(CoercionError.new("Value of type '#{value.class}' cannot be coerced to #{type} Struct.")) unless value.is_a?(Hash)
|
21
21
|
|
22
22
|
Success.new(type.from_hash!(HashTransformer.new.deep_stringify_keys(value)))
|
23
23
|
rescue ArgumentError => e
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module Typed
|
4
|
+
module Coercion
|
5
|
+
class TypedArrayCoercer < Coercer
|
6
|
+
extend T::Generic
|
7
|
+
|
8
|
+
Target = type_member { {fixed: T::Array[T.untyped]} }
|
9
|
+
|
10
|
+
sig { override.params(type: Field::Type).returns(T::Boolean) }
|
11
|
+
def used_for_type?(type)
|
12
|
+
type.is_a?(T::Types::TypedArray)
|
13
|
+
end
|
14
|
+
|
15
|
+
sig { override.params(type: Field::Type, value: Value).returns(Result[Target, CoercionError]) }
|
16
|
+
def coerce(type:, value:)
|
17
|
+
return Failure.new(CoercionError.new("Field type must be a T::Array.")) unless type.is_a?(T::Types::TypedArray)
|
18
|
+
return Failure.new(CoercionError.new("Value must be an Array.")) unless value.is_a?(Array)
|
19
|
+
|
20
|
+
return Success.new(value) if type.recursively_valid?(value)
|
21
|
+
|
22
|
+
coerced_results = value.map do |item|
|
23
|
+
Coercion.coerce(type: type.type.raw_type, value: item)
|
24
|
+
end
|
25
|
+
|
26
|
+
if coerced_results.all?(&:success?)
|
27
|
+
Success.new(coerced_results.map(&:payload))
|
28
|
+
else
|
29
|
+
Failure.new(CoercionError.new(coerced_results.select(&:failure?).map(&:error).map(&:message).join(" | ")))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/typed/coercion.rb
CHANGED
@@ -9,13 +9,13 @@ module Typed
|
|
9
9
|
CoercerRegistry.instance.register(coercer)
|
10
10
|
end
|
11
11
|
|
12
|
-
sig { type_parameters(:U).params(
|
13
|
-
def self.coerce(
|
14
|
-
coercer = CoercerRegistry.instance.select_coercer_by(type:
|
12
|
+
sig { type_parameters(:U).params(type: Field::Type, value: Value).returns(Result[Value, CoercionError]) }
|
13
|
+
def self.coerce(type:, value:)
|
14
|
+
coercer = CoercerRegistry.instance.select_coercer_by(type: type)
|
15
15
|
|
16
16
|
return Failure.new(CoercionNotSupportedError.new) unless coercer
|
17
17
|
|
18
|
-
coercer.new.coerce(
|
18
|
+
coercer.new.coerce(type: type, value: value)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
data/lib/typed/field.rb
CHANGED
@@ -6,8 +6,10 @@ module Typed
|
|
6
6
|
|
7
7
|
include ActsAsComparable
|
8
8
|
|
9
|
+
Type = T.type_alias { T.any(T::Class[T.anything], T::Types::Base) }
|
10
|
+
|
9
11
|
const :name, Symbol
|
10
|
-
const :type,
|
12
|
+
const :type, Type
|
11
13
|
const :required, T::Boolean, default: true
|
12
14
|
|
13
15
|
sig { returns(T::Boolean) }
|
@@ -24,5 +26,12 @@ module Typed
|
|
24
26
|
def validate(value)
|
25
27
|
Validations::FieldTypeValidator.new.validate(field: self, value: value)
|
26
28
|
end
|
29
|
+
|
30
|
+
sig { params(value: Value).returns(T::Boolean) }
|
31
|
+
def works_with?(value)
|
32
|
+
value.class == type || T.cast(type, T::Types::Base).recursively_valid?(value) # standard:disable Style/ClassEqualityComparison
|
33
|
+
rescue TypeError
|
34
|
+
false
|
35
|
+
end
|
27
36
|
end
|
28
37
|
end
|
@@ -3,19 +3,33 @@
|
|
3
3
|
module Typed
|
4
4
|
class HashSerializer < Serializer
|
5
5
|
InputHash = T.type_alias { T::Hash[T.any(Symbol, String), T.untyped] }
|
6
|
-
OutputHash = T.type_alias { Params }
|
7
|
-
|
8
6
|
Input = type_member { {fixed: InputHash} }
|
9
|
-
Output = type_member { {fixed:
|
7
|
+
Output = type_member { {fixed: Params} }
|
8
|
+
|
9
|
+
sig { params(schema: Schema, should_serialize_values: T::Boolean).void }
|
10
|
+
def initialize(schema:, should_serialize_values: false)
|
11
|
+
@should_serialize_values = should_serialize_values
|
12
|
+
|
13
|
+
super(schema: schema)
|
14
|
+
end
|
10
15
|
|
11
16
|
sig { override.params(source: Input).returns(Result[T::Struct, DeserializeError]) }
|
12
17
|
def deserialize(source)
|
13
|
-
deserialize_from_creation_params(HashTransformer.new.deep_symbolize_keys(source))
|
18
|
+
deserialize_from_creation_params(HashTransformer.new(should_serialize_values: should_serialize_values).deep_symbolize_keys(source))
|
14
19
|
end
|
15
20
|
|
16
|
-
sig { override.params(struct: T::Struct).returns(Output) }
|
21
|
+
sig { override.params(struct: T::Struct).returns(Result[Output, SerializeError]) }
|
17
22
|
def serialize(struct)
|
18
|
-
|
23
|
+
return Failure.new(SerializeError.new("'#{struct.class}' cannot be serialized to target type of '#{schema.target}'.")) if struct.class != schema.target
|
24
|
+
|
25
|
+
hsh = schema.fields.each_with_object({}) { |field, hsh| hsh[field.name] = struct.send(field.name) }
|
26
|
+
|
27
|
+
Success.new(HashTransformer.new(should_serialize_values: should_serialize_values).deep_symbolize_keys(hsh.compact))
|
19
28
|
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
sig { returns(T::Boolean) }
|
33
|
+
attr_reader :should_serialize_values
|
20
34
|
end
|
21
35
|
end
|
@@ -20,9 +20,11 @@ module Typed
|
|
20
20
|
Failure.new(ParseError.new(format: :json))
|
21
21
|
end
|
22
22
|
|
23
|
-
sig { override.params(struct: T::Struct).returns(Output) }
|
23
|
+
sig { override.params(struct: T::Struct).returns(Result[Output, SerializeError]) }
|
24
24
|
def serialize(struct)
|
25
|
-
|
25
|
+
return Failure.new(SerializeError.new("'#{struct.class}' cannot be serialized to target type of '#{schema.target}'.")) if struct.class != schema.target
|
26
|
+
|
27
|
+
Success.new(JSON.generate(struct.serialize))
|
26
28
|
end
|
27
29
|
end
|
28
30
|
end
|
data/lib/typed/schema.rb
CHANGED
@@ -17,5 +17,15 @@ module Typed
|
|
17
17
|
end
|
18
18
|
)
|
19
19
|
end
|
20
|
+
|
21
|
+
sig { params(hash: Typed::HashSerializer::InputHash).returns(Typed::Serializer::DeserializeResult) }
|
22
|
+
def from_hash(hash)
|
23
|
+
Typed::HashSerializer.new(schema: self).deserialize(hash)
|
24
|
+
end
|
25
|
+
|
26
|
+
sig { params(json: String).returns(Typed::Serializer::DeserializeResult) }
|
27
|
+
def from_json(json)
|
28
|
+
Typed::JSONSerializer.new(schema: self).deserialize(json)
|
29
|
+
end
|
20
30
|
end
|
21
31
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module Typed
|
4
|
+
class SerializationError < StandardError
|
5
|
+
extend T::Sig
|
6
|
+
|
7
|
+
sig { returns({error: String}) }
|
8
|
+
def to_h
|
9
|
+
{error: message}
|
10
|
+
end
|
11
|
+
|
12
|
+
sig { params(_options: T::Hash[T.untyped, T.untyped]).returns(String) }
|
13
|
+
def to_json(_options = {})
|
14
|
+
JSON.generate(to_h)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/typed/serializer.rb
CHANGED
@@ -20,11 +20,11 @@ module Typed
|
|
20
20
|
@schema = schema
|
21
21
|
end
|
22
22
|
|
23
|
-
sig { abstract.params(source:
|
23
|
+
sig { abstract.params(source: Input).returns(DeserializeResult) }
|
24
24
|
def deserialize(source)
|
25
25
|
end
|
26
26
|
|
27
|
-
sig { abstract.params(struct: T::Struct).returns(Output) }
|
27
|
+
sig { abstract.params(struct: T::Struct).returns(Result[Output, SerializeError]) }
|
28
28
|
def serialize(struct)
|
29
29
|
end
|
30
30
|
|
@@ -35,18 +35,16 @@ module Typed
|
|
35
35
|
results = schema.fields.map do |field|
|
36
36
|
value = creation_params[field.name]
|
37
37
|
|
38
|
-
if value.nil?
|
38
|
+
if value.nil? || field.works_with?(value)
|
39
39
|
field.validate(value)
|
40
|
-
|
41
|
-
coercion_result = Coercion.coerce(
|
40
|
+
else
|
41
|
+
coercion_result = Coercion.coerce(type: field.type, value: value)
|
42
42
|
|
43
43
|
if coercion_result.success?
|
44
44
|
field.validate(coercion_result.payload)
|
45
45
|
else
|
46
46
|
Failure.new(Validations::ValidationError.new(coercion_result.error.message))
|
47
47
|
end
|
48
|
-
else
|
49
|
-
field.validate(value)
|
50
48
|
end
|
51
49
|
end
|
52
50
|
|
@@ -9,7 +9,7 @@ module Typed
|
|
9
9
|
|
10
10
|
sig { override.params(field: Field, value: Value).returns(ValidationResult) }
|
11
11
|
def validate(field:, value:)
|
12
|
-
if field.
|
12
|
+
if field.works_with?(value)
|
13
13
|
Success.new(ValidatedValue.new(name: field.name, value: value))
|
14
14
|
elsif field.required? && value.nil?
|
15
15
|
Failure.new(RequiredFieldError.new(field_name: field.name))
|
@@ -5,7 +5,7 @@ module Typed
|
|
5
5
|
class TypeMismatchError < ValidationError
|
6
6
|
extend T::Sig
|
7
7
|
|
8
|
-
sig { params(field_name: Symbol, field_type:
|
8
|
+
sig { params(field_name: Symbol, field_type: Field::Type, given_type: T::Class[T.anything]).void }
|
9
9
|
def initialize(field_name:, field_type:, given_type:)
|
10
10
|
super("Invalid type given to #{field_name}. Expected #{field_type}, got #{given_type}.")
|
11
11
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sorbet-schema
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Max VelDink
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-03-
|
11
|
+
date: 2024-03-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sorbet-result
|
@@ -87,20 +87,25 @@ files:
|
|
87
87
|
- lib/sorbet-schema/hash_transformer.rb
|
88
88
|
- lib/sorbet-schema/version.rb
|
89
89
|
- lib/typed/coercion.rb
|
90
|
+
- lib/typed/coercion/boolean_coercer.rb
|
90
91
|
- lib/typed/coercion/coercer.rb
|
91
92
|
- lib/typed/coercion/coercer_registry.rb
|
92
93
|
- lib/typed/coercion/coercion_error.rb
|
93
94
|
- lib/typed/coercion/coercion_not_supported_error.rb
|
95
|
+
- lib/typed/coercion/enum_coercer.rb
|
94
96
|
- lib/typed/coercion/float_coercer.rb
|
95
97
|
- lib/typed/coercion/integer_coercer.rb
|
96
98
|
- lib/typed/coercion/string_coercer.rb
|
97
99
|
- lib/typed/coercion/struct_coercer.rb
|
100
|
+
- lib/typed/coercion/typed_array_coercer.rb
|
98
101
|
- lib/typed/deserialize_error.rb
|
99
102
|
- lib/typed/field.rb
|
100
103
|
- lib/typed/hash_serializer.rb
|
101
104
|
- lib/typed/json_serializer.rb
|
102
105
|
- lib/typed/parse_error.rb
|
103
106
|
- lib/typed/schema.rb
|
107
|
+
- lib/typed/serialization_error.rb
|
108
|
+
- lib/typed/serialize_error.rb
|
104
109
|
- lib/typed/serializer.rb
|
105
110
|
- lib/typed/validations.rb
|
106
111
|
- lib/typed/validations/field_type_validator.rb
|