Skip to content

Commit 96b2ef6

Browse files
committed
Merge branch 'fix-schema-with-alt'
2 parents fafe8a8 + b961150 commit 96b2ef6

File tree

4 files changed

+31
-11
lines changed

4 files changed

+31
-11
lines changed

lib/norm.ex

+1-1
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ defmodule Norm do
450450
...> )
451451
%{age: 31, name: "chris"}
452452
"""
453-
def schema(input) do
453+
def schema(input) when is_map(input) do
454454
Schema.build(input)
455455
end
456456

lib/norm/schema.ex

+16-9
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,15 @@ defmodule Norm.Schema do
4040
{:error, [error(path, input, "not a map")]}
4141
end
4242

43-
def conform(%{specs: specs, struct: struct}, input, path) when not is_nil(struct) do
44-
if Map.get(input, :__struct__) == struct do
45-
check_specs(specs, input, path)
43+
def conform(%{specs: specs, struct: target}, input, path) when not is_nil(target) do
44+
# Ensure we're mapping the correct struct
45+
if Map.get(input, :__struct__) == target do
46+
with {:ok, conformed} <- check_specs(specs, input, path) do
47+
{:ok, struct(target, conformed)}
48+
end
4649
else
4750
short_name =
48-
struct
51+
target
4952
|> Atom.to_string
5053
|> String.replace("Elixir.", "")
5154

@@ -71,18 +74,22 @@ defmodule Norm.Schema do
7174
(actual_keys -- expected_keys)
7275
|> Enum.map(fn key -> error(path ++ [key], input, ":unexpected") end)
7376

74-
errors =
77+
results =
7578
specs
7679
|> Enum.map(& check_spec(&1, input, path))
77-
|> Enum.filter(fn {_, {result, _}} -> result == :error end)
78-
|> Enum.flat_map(fn {_, {_, errors}} -> errors end)
80+
|> Enum.reduce(%{ok: [], error: []}, fn {key, {result, conformed}}, acc ->
81+
Map.put(acc, result, acc[result] ++ [{key, conformed}])
82+
end)
7983

80-
errors = errors ++ unexpected_key_errors
84+
errors =
85+
results.error
86+
|> Enum.flat_map(fn {_, error} -> error end)
87+
|> Enum.concat(unexpected_key_errors)
8188

8289
if Enum.any?(errors) do
8390
{:error, errors}
8491
else
85-
{:ok, input}
92+
{:ok, Enum.into(results.ok, %{})}
8693
end
8794
end
8895

lib/norm/spec/alt.ex

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ defmodule Norm.Spec.Alt do
1010
result =
1111
specs
1212
|> Enum.map(fn {name, spec} ->
13-
case Conformable.conform(spec, input, [name | path]) do
13+
case Conformable.conform(spec, input, path ++ [name]) do
1414
{:ok, i} ->
1515
{:ok, {name, i}}
1616

test/norm/schema_test.exs

+13
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,19 @@ defmodule Norm.SchemaTest do
9999
]
100100
end
101101

102+
test "can have nested alts" do
103+
s = schema(%{a: alt(bool: spec(is_boolean()), int: spec(is_integer()))})
104+
105+
assert %{a: {:bool, true}} == conform!(%{a: true}, s)
106+
assert %{a: {:bool, false}} == conform!(%{a: false}, s)
107+
assert %{a: {:int, 123}} == conform!(%{a: 123}, s)
108+
assert {:error, errors} = conform(%{a: "test"}, s)
109+
assert errors == [
110+
"val: \"test\" fails: is_boolean() in: :a/:bool",
111+
"val: \"test\" fails: is_integer() in: :a/:int"
112+
]
113+
end
114+
102115
test "breaks if the input has more keys then we've specified" do
103116
user_schema = schema(%{
104117
name: spec(is_binary())

0 commit comments

Comments
 (0)