Skip to content

Commit

Permalink
Make HashMap coerce values, not just validate
Browse files Browse the repository at this point in the history
  • Loading branch information
ismasan committed Nov 6, 2024
1 parent 901684e commit f2ac126
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 16 deletions.
22 changes: 10 additions & 12 deletions lib/plumb/hash_map.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,19 @@ def initialize(key_type, value_type)
def call(result)
return result.invalid(errors: 'must be a Hash') unless result.value.is_a?(::Hash)

failed = result.value.lazy.filter_map do |key, value|
errors = {}

parsed = result.value.each.with_object({}) do |(key, value), memo|
key_r = @key_type.resolve(key)
value_r = @value_type.resolve(value)
if !key_r.valid?
[:key, key, key_r]
elsif !value_r.valid?
[:value, value, value_r]
end
errs = []
errs << "key #{key_r.errors}" unless key_r.valid?
errs << "value #{value_r.value.inspect} #{value_r.errors}" unless value_r.valid?
errors[key] = errs unless errs.empty?
memo[key_r.value] = value_r.value
end
if (first = failed.next)
field, val, halt = failed.first
result.invalid(errors: "#{field} #{val.inspect} #{halt.errors}")
end
rescue StopIteration
result

errors.empty? ? result.valid(parsed) : result.invalid(errors:)
end

def filtered
Expand Down
15 changes: 11 additions & 4 deletions spec/types_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -648,8 +648,7 @@ def test(foo, bar = 1, opt: 2)
end

specify 'pattern matching' do
symbol = Types::Symbol
three_chars = Types::String.policy(size: 3)
Types::String.policy(size: 3)
[:ok, 'sup'] => [symbol => sym, three_chars => chars]

expect(sym).to eq(:ok)
Expand Down Expand Up @@ -1028,17 +1027,25 @@ def test(foo, bar = 1, opt: 2)
end

describe '#[key_type, value_type] (Hash Map)' do
specify do
deep_hash = Types::Hash[
Types::String.transform(Symbol, &:to_sym),
Types::Any.defer { deep_hash } | Types::Any
]
assert_result(deep_hash.resolve('a' => 1, 'b' => 2), { a: 1, b: 2 }, true)
end

it 'validates keys and values' do
s1 = Types::Hash[Types::String, Types::Integer]
expect(s1.metadata).to eq(type: Hash)
assert_result(s1.resolve('a' => 1, 'b' => 2), { 'a' => 1, 'b' => 2 }, true)
s1.resolve(a: 1, 'b' => 2).tap do |result|
assert_result(result, { a: 1, 'b' => 2 }, false)
expect(result.errors).to eq('key :a Must be a String')
expect(result.errors).to eq(a: ['key Must be a String'])
end
s1.resolve('a' => 1, 'b' => {}).tap do |result|
assert_result(result, { 'a' => 1, 'b' => {} }, false)
expect(result.errors).to eq('value {} Must be a Integer')
expect(result.errors).to eq('b' => ['value {} Must be a Integer'])
end
assert_result(s1.present.resolve({}), {}, false)
end
Expand Down

0 comments on commit f2ac126

Please sign in to comment.