Skip to content

Commit

Permalink
feat: Add option to serialize values to HashSerializer
Browse files Browse the repository at this point in the history
  • Loading branch information
abhasavanich authored Mar 13, 2024
1 parent 55c2da7 commit 710d365
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 14 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
/tmp/
*.gem
.DS_Store
.idea/*
27 changes: 25 additions & 2 deletions lib/sorbet-schema/hash_transformer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,40 @@
class HashTransformer
extend T::Sig

sig { params(should_serialize_values: T::Boolean).void }
def initialize(should_serialize_values: false)
@should_serialize_values = should_serialize_values
end

sig { params(hash: T::Hash[T.untyped, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
def deep_symbolize_keys(hash)
hash.each_with_object({}) do |(key, value), result|
result[key.to_sym] = value.is_a?(Hash) ? deep_symbolize_keys(value) : value
result[key.to_sym] = transform_value(value, hash_transformation_method: :deep_symbolize_keys)
end
end

sig { params(hash: T::Hash[T.untyped, T.untyped]).returns(T::Hash[String, T.untyped]) }
def deep_stringify_keys(hash)
hash.each_with_object({}) do |(key, value), result|
result[key.to_s] = value.is_a?(Hash) ? deep_stringify_keys(value) : value
result[key.to_s] = transform_value(value, hash_transformation_method: :deep_stringify_keys)
end
end

private

sig { returns(T::Boolean) }
attr_reader :should_serialize_values

sig { params(value: T.untyped, hash_transformation_method: Symbol).returns(T.untyped) }
def transform_value(value, hash_transformation_method:)
if value.is_a?(Hash)
send(hash_transformation_method, value)
elsif value.is_a?(Array)
value.map { |inner_val| transform_value(inner_val, hash_transformation_method: hash_transformation_method) }
elsif value.respond_to?(:serialize) && should_serialize_values
value.serialize
else
value
end
end
end
16 changes: 14 additions & 2 deletions lib/typed/hash_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,26 @@ class HashSerializer < Serializer
Input = type_member { {fixed: InputHash} }
Output = type_member { {fixed: OutputHash} }

sig { params(schema: Schema, should_serialize_values: T::Boolean).void }
def initialize(schema:, should_serialize_values: false)
@should_serialize_values = should_serialize_values

super(schema: schema)
end

sig { override.params(source: Input).returns(Result[T::Struct, DeserializeError]) }
def deserialize(source)
deserialize_from_creation_params(HashTransformer.new.deep_symbolize_keys(source))
deserialize_from_creation_params(HashTransformer.new(should_serialize_values: should_serialize_values).deep_symbolize_keys(source))
end

sig { override.params(struct: T::Struct).returns(Output) }
def serialize(struct)
HashTransformer.new.deep_symbolize_keys(struct.serialize)
HashTransformer.new(should_serialize_values: should_serialize_values).deep_symbolize_keys(struct.serialize)
end

private

sig { returns(T::Boolean) }
attr_reader :should_serialize_values
end
end
68 changes: 58 additions & 10 deletions test/hash_transformer_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@

class HashTransformerTest < Minitest::Test
def setup
# standard:disable Style/HashSyntax
@test_hash = {
"test" => 1,
another: 1,
deeper: {
anotherhash: 2,
"test" => TestEnums::EnumOne,
:another => 1,
:deeper => {
:anotherhash => 2,
"deeperagain" => {
"value" => 3
"value" => TestEnums::EnumThree,
"boolean" => false,
"date" => Date.new(1776, 7, 4),
"array" => [1, TestEnums::EnumOne, {"verydeep" => 1}]
}
}
}
Expand All @@ -19,31 +21,77 @@ def setup

def test_deep_symbolize_keys_symbolizes_all_keys
expected_hash = {
test: 1,
test: TestEnums::EnumOne,
another: 1,
deeper: {
anotherhash: 2,
deeperagain: {
value: 3
value: TestEnums::EnumThree,
boolean: false,
date: Date.new(1776, 7, 4),
array: [1, TestEnums::EnumOne, {verydeep: 1}]
}
}
}

assert_equal(expected_hash, @transformer.deep_symbolize_keys(@test_hash))
end

def test_deep_symbolize_keys_serialize_values_symbolizes_all_keys_and_serializes_values
transformer = HashTransformer.new(should_serialize_values: true)

expected_hash = {
test: "1",
another: 1,
deeper: {
anotherhash: 2,
deeperagain: {
value: "3",
boolean: false,
date: Date.new(1776, 7, 4),
array: [1, "1", {verydeep: 1}]
}
}
}

assert_equal(expected_hash, transformer.deep_symbolize_keys(@test_hash))
end

def test_deep_stringify_keys_stringifies_all_keys
expected_hash = {
"test" => 1,
"test" => TestEnums::EnumOne,
"another" => 1,
"deeper" => {
"anotherhash" => 2,
"deeperagain" => {
"value" => 3
"value" => TestEnums::EnumThree,
"boolean" => false,
"date" => Date.new(1776, 7, 4),
"array" => [1, TestEnums::EnumOne, {"verydeep" => 1}]
}
}
}

assert_equal(expected_hash, @transformer.deep_stringify_keys(@test_hash))
end

def test_deep_stringify_keys_serialize_values_stringifies_all_keys_and_serializes_values
transformer = HashTransformer.new(should_serialize_values: true)

expected_hash = {
"test" => "1",
"another" => 1,
"deeper" => {
"anotherhash" => 2,
"deeperagain" => {
"value" => "3",
"boolean" => false,
"date" => Date.new(1776, 7, 4),
"array" => [1, "1", {"verydeep" => 1}]
}
}
}

assert_equal(expected_hash, transformer.deep_stringify_keys(@test_hash))
end
end
9 changes: 9 additions & 0 deletions test/support/test_enums.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# typed: true

class TestEnums < T::Enum
enums do
EnumOne = new("1")
EnumTwo = new("2")
EnumThree = new("3")
end
end

0 comments on commit 710d365

Please sign in to comment.