Repository: keathley/norm (elixir)

Issue:
Collections on selectors
Great work! 

I'm trying to validate collections of maps `coll_of()` based on selections, for example

````elixir
  conform(%{a: [%{b: true}], 
    selection(
        schema(%{a: 
          one_of(
            schema(%{b: spec(is_boolean())})
            )
          }
        ),
        [a: [:b]]
    )
  )
```` 
but selections doesn't allows collections.
It's there a workaround? 

Pull request description:
Always return selection errors
Closes #28 

Patch:
diff --git a/lib/norm/spec/selection.ex b/lib/norm/spec/selection.ex
index 7a9062c..d0c6964 100644
--- a/lib/norm/spec/selection.ex
+++ b/lib/norm/spec/selection.ex
@@ -73,21 +73,23 @@ defmodule Norm.Spec.Selection do
     alias Norm.Conformer
     alias Norm.Conformer.Conformable
 
+    def conform(_, input, path) when not is_map(input) do
+      {:error, [Conformer.error(path, input, "not a map")]}
+    end
+
     def conform(%{required: required, schema: schema}, input, path) do
-      with {:ok, conformed} <- Conformable.conform(schema, input, path) do
-        errors = ensure_keys(required, conformed, path, [])
-
-        if Enum.any?(errors) do
-          errors =
-            errors
-            |> Enum.flat_map(fn {_, errors} -> errors end)
-
-          {:error, errors}
-        else
-          # We can just return the conformed values here because we know that
-          # everything is in there.
-          {:ok, conformed}
-        end
+      case Conformable.conform(schema, input, path) do
+        {:ok, conformed} ->
+          errors = ensure_keys(required, conformed, path, [])
+          if Enum.any?(errors) do
+            {:error, errors}
+          else
+            {:ok, conformed}
+          end
+
+        {:error, conforming_errors} ->
+          errors = ensure_keys(required, input, path, [])
+          {:error, conforming_errors ++ errors}
       end
     end
 
@@ -117,7 +119,7 @@ defmodule Norm.Spec.Selection do
       if Map.has_key?(conformed, key) do
         :ok
       else
-        {:error, [Conformer.error(path ++ [key], conformed, ":required")]}
+        Conformer.error(path ++ [key], conformed, ":required")
       end
     end
   end


Test patch:
diff --git a/test/norm/selection_test.exs b/test/norm/selection_test.exs
index 36a9d05..5d97911 100644
--- a/test/norm/selection_test.exs
+++ b/test/norm/selection_test.exs
@@ -77,6 +77,18 @@ defmodule Norm.SelectionTest do
       refute valid?(%{name: "chris", age: 31}, selection(user_schema()))
     end
 
+    test "always returns missing keys even if the schema errors" do
+      s = schema(%{
+        a: coll_of(selection(schema(%{b: spec(is_boolean())}))),
+        c: spec(is_boolean())
+      })
+      assert {:error, errors} = conform(%{a: [%{b: "no_bool"}]}, selection(s, [:c]))
+      assert errors == [
+        %{input: "no_bool", path: [:a, 0, :b], spec: "is_boolean()"},
+        %{input: %{a: [%{b: "no_bool"}]}, path: [:c], spec: ":required"},
+      ]
+    end
+
     test "errors if there are keys that aren't specified in a schema" do
       assert_raise Norm.SpecError, fn ->
         selection(schema(%{age: spec(is_integer())}), [:name])