sorbet-schema 0.5.1 → 0.7.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 +30 -0
- data/Gemfile.lock +1 -1
- data/lib/sorbet-schema/hash_transformer.rb +12 -35
- data/lib/sorbet-schema/serialize_value.rb +20 -0
- data/lib/sorbet-schema/version.rb +1 -1
- data/lib/sorbet-schema.rb +2 -1
- data/lib/typed/coercion/coercer_registry.rb +3 -1
- data/lib/typed/coercion/struct_coercer.rb +8 -34
- data/lib/typed/coercion/symbol_coercer.rb +27 -0
- data/lib/typed/coercion/typed_hash_coercer.rb +50 -0
- data/lib/typed/field.rb +29 -4
- data/lib/typed/hash_serializer.rb +2 -2
- data/lib/typed/json_serializer.rb +1 -1
- data/lib/typed/schema.rb +2 -2
- data/lib/typed/serializer.rb +31 -5
- data/lib/typed/validations/field_type_validator.rb +6 -2
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d2adb1981aeb5218f9d137f472bd602a2b81bbe6e54166e2974f1c157f92d50a
|
4
|
+
data.tar.gz: 1f4ee3983069f2674aab270bb7d138ce3fc611154a4d5c37581b6698ac6f3189
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d8b876a03ee53cafde5a9bbd9f46823d517aa6d69a3417a4e3c04591f1471066bc2912c2c26e00ba9e9294642912e4a4d58dafada77e8206685b4f726c23ae52
|
7
|
+
data.tar.gz: 9bf510fe1ef9e4b56ef9206ed1c5ea43bcb3934b1749c427afe76291a9b09871383d443f226f15cac733c39540180c2eed810d653b1728f73e11a8572f5b80be
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,36 @@ 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.7.0](https://github.com/maxveldink/sorbet-schema/compare/v0.6.0...v0.7.0) (2024-07-08)
|
8
|
+
|
9
|
+
|
10
|
+
### ⚠ BREAKING CHANGES
|
11
|
+
|
12
|
+
* Fix mis-serializing hash keys that were suppose to be strings ([#111](https://github.com/maxveldink/sorbet-schema/issues/111))
|
13
|
+
|
14
|
+
### Bug Fixes
|
15
|
+
|
16
|
+
* Fix mis-serializing hash keys that were suppose to be strings ([#111](https://github.com/maxveldink/sorbet-schema/issues/111)) ([485a6c7](https://github.com/maxveldink/sorbet-schema/commit/485a6c7a83b9e70c731930d8406925304efa04a8))
|
17
|
+
|
18
|
+
## [0.6.0](https://github.com/maxveldink/sorbet-schema/compare/v0.5.1...v0.6.0) (2024-07-07)
|
19
|
+
|
20
|
+
|
21
|
+
### ⚠ BREAKING CHANGES
|
22
|
+
|
23
|
+
* implement default handling for fields ([#105](https://github.com/maxveldink/sorbet-schema/issues/105))
|
24
|
+
|
25
|
+
### Features
|
26
|
+
|
27
|
+
* implement default handling for fields ([#105](https://github.com/maxveldink/sorbet-schema/issues/105)) ([054d59f](https://github.com/maxveldink/sorbet-schema/commit/054d59ff92c68b272d495a0816370b9a890f0f50))
|
28
|
+
* implement SymbolCoercer ([#109](https://github.com/maxveldink/sorbet-schema/issues/109)) ([422a995](https://github.com/maxveldink/sorbet-schema/commit/422a9957177039a3dde5c4daa41d597fd44f2b48))
|
29
|
+
* implement TypedHashCoercer ([#110](https://github.com/maxveldink/sorbet-schema/issues/110)) ([6d64db7](https://github.com/maxveldink/sorbet-schema/commit/6d64db7fcef8af56cb96f1ee6c42ba1e3ce076c3))
|
30
|
+
* support T.any for deserialization ([#107](https://github.com/maxveldink/sorbet-schema/issues/107)) ([c0c2ca3](https://github.com/maxveldink/sorbet-schema/commit/c0c2ca369abef136943e633b7987decad7291d98))
|
31
|
+
|
32
|
+
|
33
|
+
### Bug Fixes
|
34
|
+
|
35
|
+
* default value set to true causes undefined method [] error ([#108](https://github.com/maxveldink/sorbet-schema/issues/108)) ([6829bbf](https://github.com/maxveldink/sorbet-schema/commit/6829bbf8b6bf47db51209e9874608b7e10c38b8e))
|
36
|
+
|
7
37
|
## [0.5.1](https://github.com/maxveldink/sorbet-schema/compare/v0.5.0...v0.5.1) (2024-06-26)
|
8
38
|
|
9
39
|
|
data/Gemfile.lock
CHANGED
@@ -4,44 +4,21 @@
|
|
4
4
|
# This is a simplified version of ActiveSupport's Key Hash extension
|
5
5
|
# https://github.com/rails/rails/blob/main/activesupport/lib/active_support/core_ext/hash/keys.rb
|
6
6
|
class HashTransformer
|
7
|
-
|
7
|
+
class << self
|
8
|
+
extend T::Sig
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
sig { params(hash: T::Hash[T.untyped, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
15
|
-
def deep_symbolize_keys(hash)
|
16
|
-
hash.each_with_object({}) do |(key, value), result|
|
17
|
-
result[key.to_sym] = transform_value(value, hash_transformation_method: :deep_symbolize_keys)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
sig { params(hash: T::Hash[T.untyped, T.untyped]).returns(T::Hash[String, T.untyped]) }
|
22
|
-
def deep_stringify_keys(hash)
|
23
|
-
hash.each_with_object({}) do |(key, value), result|
|
24
|
-
result[key.to_s] = transform_value(value, hash_transformation_method: :deep_stringify_keys)
|
10
|
+
sig { params(hash: T::Hash[T.untyped, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
11
|
+
def symbolize_keys(hash)
|
12
|
+
hash.each_with_object({}) do |(key, value), result|
|
13
|
+
result[key.to_sym] = value
|
14
|
+
end
|
25
15
|
end
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
sig { returns(T::Boolean) }
|
31
|
-
attr_reader :should_serialize_values
|
32
16
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
17
|
+
sig { params(hash: T::Hash[T.untyped, T.untyped]).returns(T::Hash[T.untyped, T.untyped]) }
|
18
|
+
def serialize_values(hash)
|
19
|
+
hash.each_with_object({}) do |(key, value), result|
|
20
|
+
result[key] = SerializeValue.serialize(value)
|
21
|
+
end
|
45
22
|
end
|
46
23
|
end
|
47
24
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
class SerializeValue
|
4
|
+
extend T::Sig
|
5
|
+
|
6
|
+
sig { params(value: T.untyped).returns(T.untyped) }
|
7
|
+
def self.serialize(value)
|
8
|
+
if value.is_a?(Hash)
|
9
|
+
HashTransformer.serialize_values(value)
|
10
|
+
elsif value.is_a?(Array)
|
11
|
+
value.map { |item| serialize(item) }
|
12
|
+
elsif value.is_a?(T::Struct)
|
13
|
+
value.serialize_to(:hash).payload_or(value)
|
14
|
+
elsif value.respond_to?(:serialize)
|
15
|
+
value.serialize
|
16
|
+
else
|
17
|
+
value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/sorbet-schema.rb
CHANGED
@@ -16,10 +16,11 @@ loader.inflector.inflect(
|
|
16
16
|
)
|
17
17
|
loader.setup
|
18
18
|
|
19
|
-
# We don't want to place
|
19
|
+
# We don't want to place these in the `Typed` module.
|
20
20
|
# `sorbet-schema` is a directory that is not autoloaded
|
21
21
|
# but contains extensions, so we need to manually require it.
|
22
22
|
require_relative "sorbet-schema/hash_transformer"
|
23
|
+
require_relative "sorbet-schema/serialize_value"
|
23
24
|
|
24
25
|
# We want to add a default `schema` method to structs
|
25
26
|
# that will guarentee a schema can be created for use
|
@@ -14,13 +14,15 @@ module Typed
|
|
14
14
|
DEFAULT_COERCERS = T.let(
|
15
15
|
[
|
16
16
|
StringCoercer,
|
17
|
+
SymbolCoercer,
|
17
18
|
BooleanCoercer,
|
18
19
|
IntegerCoercer,
|
19
20
|
FloatCoercer,
|
20
21
|
DateCoercer,
|
21
22
|
EnumCoercer,
|
22
23
|
StructCoercer,
|
23
|
-
TypedArrayCoercer
|
24
|
+
TypedArrayCoercer,
|
25
|
+
TypedHashCoercer
|
24
26
|
],
|
25
27
|
Registry
|
26
28
|
)
|
@@ -21,41 +21,15 @@ module Typed
|
|
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)
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
if value[name].nil?
|
33
|
-
# if the value is nil but the type is nilable, no need to coerce
|
34
|
-
next if attribute_type.respond_to?(:valid?) && attribute_type.valid?(value[name])
|
35
|
-
|
36
|
-
return Typed::Failure.new(CoercionError.new("#{name} is required but nil given"))
|
37
|
-
end
|
38
|
-
|
39
|
-
# now that we've done the nil check, we can unwrap the nilable type to get the raw type
|
40
|
-
simple_attribute_type = attribute_type.respond_to?(:unwrap_nilable) ? attribute_type.unwrap_nilable : attribute_type
|
41
|
-
|
42
|
-
# if the prop is a struct, we need to recursively coerce it
|
43
|
-
if simple_attribute_type.respond_to?(:raw_type) && simple_attribute_type.raw_type <= T::Struct
|
44
|
-
Typed::HashSerializer
|
45
|
-
.new(schema: simple_attribute_type.raw_type.schema)
|
46
|
-
.deserialize(value[name])
|
47
|
-
.and_then { |struct| Typed::Success.new(values[name] = struct) }
|
48
|
-
.on_error { |error| return Typed::Failure.new(CoercionError.new("Nested hash for #{type} could not be coerced to #{name}, error: #{error}")) }
|
49
|
-
else
|
50
|
-
value = HashTransformer.new.deep_symbolize_keys(value)
|
51
|
-
|
52
|
-
Coercion
|
53
|
-
.coerce(type: attribute_type, value: value[name])
|
54
|
-
.and_then { |coerced_value| Typed::Success.new(values[name] = coerced_value) }
|
55
|
-
end
|
24
|
+
deserialization_result = T.cast(type, T::Types::Simple)
|
25
|
+
.raw_type
|
26
|
+
.deserialize_from(:hash, value)
|
27
|
+
|
28
|
+
if deserialization_result.success?
|
29
|
+
deserialization_result
|
30
|
+
else
|
31
|
+
Failure.new(CoercionError.new(deserialization_result.error.message))
|
56
32
|
end
|
57
|
-
|
58
|
-
Success.new(type.raw_type.new(values))
|
59
33
|
rescue ArgumentError, RuntimeError
|
60
34
|
Failure.new(CoercionError.new("Given hash could not be coerced to #{type}."))
|
61
35
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module Typed
|
4
|
+
module Coercion
|
5
|
+
class SymbolCoercer < Coercer
|
6
|
+
extend T::Generic
|
7
|
+
|
8
|
+
Target = type_member { {fixed: Symbol} }
|
9
|
+
|
10
|
+
sig { override.params(type: T::Types::Base).returns(T::Boolean) }
|
11
|
+
def used_for_type?(type)
|
12
|
+
type == T::Utils.coerce(Symbol)
|
13
|
+
end
|
14
|
+
|
15
|
+
sig { override.params(type: T::Types::Base, value: Value).returns(Result[Target, CoercionError]) }
|
16
|
+
def coerce(type:, value:)
|
17
|
+
return Failure.new(CoercionError.new("Type must be a Symbol.")) unless used_for_type?(type)
|
18
|
+
|
19
|
+
if value.respond_to?(:to_sym)
|
20
|
+
Success.new(value.to_sym)
|
21
|
+
else
|
22
|
+
Failure.new(CoercionError.new("Value cannot be coerced into Symbol. Consider adding a #to_sym implementation."))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module Typed
|
4
|
+
module Coercion
|
5
|
+
class TypedHashCoercer < Coercer
|
6
|
+
extend T::Generic
|
7
|
+
|
8
|
+
Target = type_member { {fixed: T::Hash[T.untyped, T.untyped]} }
|
9
|
+
|
10
|
+
sig { override.params(type: T::Types::Base).returns(T::Boolean) }
|
11
|
+
def used_for_type?(type)
|
12
|
+
type.is_a?(T::Types::TypedHash)
|
13
|
+
end
|
14
|
+
|
15
|
+
sig { override.params(type: T::Types::Base, value: Value).returns(Result[Target, CoercionError]) }
|
16
|
+
def coerce(type:, value:)
|
17
|
+
return Failure.new(CoercionError.new("Field type must be a T::Hash.")) unless used_for_type?(type)
|
18
|
+
return Failure.new(CoercionError.new("Value must be a Hash.")) unless value.is_a?(Hash)
|
19
|
+
|
20
|
+
return Success.new(value) if type.recursively_valid?(value)
|
21
|
+
|
22
|
+
coerced_hash = {}
|
23
|
+
errors = []
|
24
|
+
|
25
|
+
value.each do |k, v|
|
26
|
+
key_result = Coercion.coerce(type: T::Utils.coerce(T.cast(type, T::Types::TypedHash).type.types.first), value: k)
|
27
|
+
value_result = Coercion.coerce(type: T::Utils.coerce(T.cast(type, T::Types::TypedHash).type.types.last), value: v)
|
28
|
+
|
29
|
+
if key_result.success? && value_result.success?
|
30
|
+
coerced_hash[key_result.payload] = value_result.payload
|
31
|
+
else
|
32
|
+
if key_result.failure?
|
33
|
+
errors << key_result.error
|
34
|
+
end
|
35
|
+
|
36
|
+
if value_result.failure?
|
37
|
+
errors << value_result.error
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
if errors.empty?
|
43
|
+
Success.new(coerced_hash)
|
44
|
+
else
|
45
|
+
Failure.new(CoercionError.new(errors.map(&:message).join(" | ")))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/typed/field.rb
CHANGED
@@ -12,6 +12,9 @@ module Typed
|
|
12
12
|
sig { returns(T::Types::Base) }
|
13
13
|
attr_reader :type
|
14
14
|
|
15
|
+
sig { returns(T.untyped) }
|
16
|
+
attr_reader :default
|
17
|
+
|
15
18
|
sig { returns(T::Boolean) }
|
16
19
|
attr_reader :required
|
17
20
|
|
@@ -22,15 +25,36 @@ module Typed
|
|
22
25
|
params(
|
23
26
|
name: Symbol,
|
24
27
|
type: T.any(T::Class[T.anything], T::Types::Base),
|
25
|
-
|
28
|
+
optional: T::Boolean,
|
29
|
+
default: T.untyped,
|
26
30
|
inline_serializer: T.nilable(InlineSerializer)
|
27
31
|
).void
|
28
32
|
end
|
29
|
-
def initialize(name:, type:,
|
33
|
+
def initialize(name:, type:, optional: false, default: nil, inline_serializer: nil)
|
30
34
|
@name = name
|
31
|
-
|
32
|
-
@required = required
|
35
|
+
# TODO: Guarentee type signature of the serializer will be valid
|
33
36
|
@inline_serializer = inline_serializer
|
37
|
+
|
38
|
+
coerced_type = T::Utils.coerce(type)
|
39
|
+
|
40
|
+
if coerced_type.valid?(nil)
|
41
|
+
@required = T.let(false, T::Boolean)
|
42
|
+
@type = T.let(T.unsafe(coerced_type).unwrap_nilable, T::Types::Base)
|
43
|
+
else
|
44
|
+
@required = true
|
45
|
+
@type = coerced_type
|
46
|
+
end
|
47
|
+
|
48
|
+
if optional
|
49
|
+
@required = false
|
50
|
+
end
|
51
|
+
|
52
|
+
if !default.nil? && @type.valid?(default)
|
53
|
+
@default = T.let(default, T.untyped)
|
54
|
+
@required = false
|
55
|
+
elsif !default.nil? && @required
|
56
|
+
raise ArgumentError, "Given #{default} with class of #{default.class} for default, invalid with type #{@type}"
|
57
|
+
end
|
34
58
|
end
|
35
59
|
|
36
60
|
sig { params(other: Field).returns(T.nilable(T::Boolean)) }
|
@@ -38,6 +62,7 @@ module Typed
|
|
38
62
|
name == other.name &&
|
39
63
|
type == other.type &&
|
40
64
|
required == other.required &&
|
65
|
+
default == other.default &&
|
41
66
|
inline_serializer == other.inline_serializer
|
42
67
|
end
|
43
68
|
|
@@ -15,14 +15,14 @@ module Typed
|
|
15
15
|
|
16
16
|
sig { override.params(source: Input).returns(Result[T::Struct, DeserializeError]) }
|
17
17
|
def deserialize(source)
|
18
|
-
deserialize_from_creation_params(HashTransformer.
|
18
|
+
deserialize_from_creation_params(HashTransformer.symbolize_keys(source))
|
19
19
|
end
|
20
20
|
|
21
21
|
sig { override.params(struct: T::Struct).returns(Result[Output, SerializeError]) }
|
22
22
|
def serialize(struct)
|
23
23
|
return Failure.new(SerializeError.new("'#{struct.class}' cannot be serialized to target type of '#{schema.target}'.")) if struct.class != schema.target
|
24
24
|
|
25
|
-
Success.new(serialize_from_struct(struct
|
25
|
+
Success.new(serialize_from_struct(struct:, should_serialize_values:))
|
26
26
|
end
|
27
27
|
|
28
28
|
private
|
@@ -24,7 +24,7 @@ module Typed
|
|
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
26
|
|
27
|
-
Success.new(JSON.generate(serialize_from_struct(struct
|
27
|
+
Success.new(JSON.generate(serialize_from_struct(struct:, should_serialize_values: true)))
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
data/lib/typed/schema.rb
CHANGED
@@ -13,7 +13,7 @@ module Typed
|
|
13
13
|
Typed::Schema.new(
|
14
14
|
target: struct,
|
15
15
|
fields: struct.props.map do |name, properties|
|
16
|
-
Typed::Field.new(name
|
16
|
+
Typed::Field.new(name:, type: properties[:type_object], default: properties.fetch(:default, nil))
|
17
17
|
end
|
18
18
|
)
|
19
19
|
end
|
@@ -34,7 +34,7 @@ module Typed
|
|
34
34
|
target: target,
|
35
35
|
fields: fields.map do |field|
|
36
36
|
if field.name == field_name
|
37
|
-
Field.new(name: field.name, type: field.type,
|
37
|
+
Field.new(name: field.name, type: field.type, default: field.default, inline_serializer: serializer)
|
38
38
|
else
|
39
39
|
field
|
40
40
|
end
|
data/lib/typed/serializer.rb
CHANGED
@@ -33,12 +33,34 @@ module Typed
|
|
33
33
|
sig { params(creation_params: Params).returns(DeserializeResult) }
|
34
34
|
def deserialize_from_creation_params(creation_params)
|
35
35
|
results = schema.fields.map do |field|
|
36
|
-
value = creation_params
|
36
|
+
value = creation_params.fetch(field.name, nil)
|
37
37
|
|
38
|
-
if value.nil?
|
38
|
+
if value.nil? && !field.default.nil?
|
39
|
+
Success.new(Validations::ValidatedValue.new(name: field.name, value: field.default))
|
40
|
+
elsif value.nil? || field.works_with?(value)
|
39
41
|
field.validate(value)
|
42
|
+
elsif field.type.class <= T::Types::Union
|
43
|
+
errors = []
|
44
|
+
validated_value = T.let(nil, T.nilable(Typed::Result[Typed::Validations::ValidatedValue, Typed::Validations::ValidationError]))
|
45
|
+
|
46
|
+
T.cast(field.type, T::Types::Union).types.each do |sub_type|
|
47
|
+
# the if clause took care of cases where value is nil so we can skip NilClass
|
48
|
+
next if sub_type.raw_type.equal?(NilClass)
|
49
|
+
|
50
|
+
coercion_result = Coercion.coerce(type: sub_type, value: value)
|
51
|
+
|
52
|
+
if coercion_result.success?
|
53
|
+
validated_value = field.validate(coercion_result.payload)
|
54
|
+
|
55
|
+
break
|
56
|
+
else
|
57
|
+
errors << Validations::ValidationError.new(coercion_result.error.message)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
validated_value.nil? ? Failure.new(Validations::ValidationError.new(errors.map(&:message).join(", "))) : validated_value
|
40
62
|
else
|
41
|
-
coercion_result = Coercion.coerce(type: field.type, value:
|
63
|
+
coercion_result = Coercion.coerce(type: field.type, value:)
|
42
64
|
|
43
65
|
if coercion_result.success?
|
44
66
|
field.validate(coercion_result.payload)
|
@@ -49,7 +71,7 @@ module Typed
|
|
49
71
|
end
|
50
72
|
|
51
73
|
Validations::ValidationResults
|
52
|
-
.new(results:
|
74
|
+
.new(results:)
|
53
75
|
.combine
|
54
76
|
.and_then do |validated_params|
|
55
77
|
Success.new(schema.target.new(**validated_params))
|
@@ -60,7 +82,11 @@ module Typed
|
|
60
82
|
def serialize_from_struct(struct:, should_serialize_values: false)
|
61
83
|
hsh = schema.fields.each_with_object({}) { |field, hsh| hsh[field.name] = field.serialize(struct.send(field.name)) }.compact
|
62
84
|
|
63
|
-
|
85
|
+
if should_serialize_values
|
86
|
+
hsh = HashTransformer.serialize_values(hsh)
|
87
|
+
end
|
88
|
+
|
89
|
+
hsh
|
64
90
|
end
|
65
91
|
end
|
66
92
|
end
|
@@ -10,11 +10,15 @@ module Typed
|
|
10
10
|
sig { override.params(field: Field, value: Value).returns(ValidationResult) }
|
11
11
|
def validate(field:, value:)
|
12
12
|
if field.works_with?(value)
|
13
|
-
Success.new(ValidatedValue.new(name: field.name, value:
|
13
|
+
Success.new(ValidatedValue.new(name: field.name, value:))
|
14
14
|
elsif field.required? && value.nil?
|
15
15
|
Failure.new(RequiredFieldError.new(field_name: field.name))
|
16
16
|
elsif field.optional? && value.nil?
|
17
|
-
|
17
|
+
if field.default.nil?
|
18
|
+
Success.new(ValidatedValue.new(name: field.name, value:))
|
19
|
+
else
|
20
|
+
Success.new(ValidatedValue.new(name: field.name, value: field.default))
|
21
|
+
end
|
18
22
|
else
|
19
23
|
Failure.new(TypeMismatchError.new(field_name: field.name, field_type: field.type, given_type: value.class))
|
20
24
|
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.7.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-
|
11
|
+
date: 2024-07-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sorbet-result
|
@@ -85,6 +85,7 @@ files:
|
|
85
85
|
- Rakefile
|
86
86
|
- lib/sorbet-schema.rb
|
87
87
|
- lib/sorbet-schema/hash_transformer.rb
|
88
|
+
- lib/sorbet-schema/serialize_value.rb
|
88
89
|
- lib/sorbet-schema/t/struct.rb
|
89
90
|
- lib/sorbet-schema/version.rb
|
90
91
|
- lib/typed/coercion.rb
|
@@ -99,7 +100,9 @@ files:
|
|
99
100
|
- lib/typed/coercion/integer_coercer.rb
|
100
101
|
- lib/typed/coercion/string_coercer.rb
|
101
102
|
- lib/typed/coercion/struct_coercer.rb
|
103
|
+
- lib/typed/coercion/symbol_coercer.rb
|
102
104
|
- lib/typed/coercion/typed_array_coercer.rb
|
105
|
+
- lib/typed/coercion/typed_hash_coercer.rb
|
103
106
|
- lib/typed/deserialize_error.rb
|
104
107
|
- lib/typed/field.rb
|
105
108
|
- lib/typed/hash_serializer.rb
|