Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@xtian
Last active August 16, 2017 15:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xtian/06e9c0dcb1e201d18010d477a2645488 to your computer and use it in GitHub Desktop.
Save xtian/06e9c0dcb1e201d18010d477a2645488 to your computer and use it in GitHub Desktop.
Ecto errors_on helper that returns atoms instead of strings.
@doc """
Helper for returning list of errors in a struct when given certain data.
## Examples
Given a User schema that lists `:name` as a required field and validates
`:password` to be safe, it would return:
iex> errors_on(%User{}, %{})
[name: :required]
You could then write your assertion like:
assert {:name, :required} in errors_on(%User{}, %{})
You can also create the changeset manually and retrieve the errors
field directly:
iex> changeset = User.changeset(%User{}, password: "password")
iex> {:password, "is unsafe"} in changeset.errors
true
"""
def errors_on(struct, data) do
struct.__struct__.changeset(struct, data)
|> Ecto.Changeset.traverse_errors(fn {_, opts} -> Keyword.pop(opts, :validation) end)
|> Enum.flat_map(fn {key, errors} -> for info <- errors, do: validation_info(key, info) end)
end
defp validation_info(key, {type, []}), do: {key, type}
defp validation_info(key, {type, opts}) do
opts = opts |> Keyword.drop([:count]) |> Enum.sort
{key, type, opts}
end
@doc """
A helper that transform changeset errors to a map of messages.
assert {:field_name, :invalid} in errors_on(%Schema{}, %{field_name: "bad value"})
assert {:field_name, :invalid} in errors_on(changeset)
"""
def errors_on(struct, data), do: struct |> struct.__struct__.changeset(data) |> errors_on()
def errors_on(changeset) do
changeset
|> Ecto.Changeset.traverse_errors(fn _, _, {error_message, opts} ->
{validation_name, opts} = Keyword.pop(opts, :validation)
{validation_name || error_message, opts}
end)
|> Enum.flat_map(fn {field, errors} ->
for error <- errors, do: List.to_tuple([field | flatten_keyword_list(error)])
end)
end
defp flatten_keyword_list(error), do: IO.inspect(error)
# defp flatten_keyword_list({key, list}) when is_list(list), do: [key | flatten_keyword_list(list)]
# defp flatten_keyword_list({key, value}), do: [key, value]
# defp validation_info(field, {nested_field, [{validation_name, []}]}) do
# {field, nested_field, validation_name}
# end
# defp validation_info(field, {nested_field, [{validation_name, opts}]}) do
# {field, nested_field, validation_name, filter_options(opts)}
# end
# defp validation_info(field, {validation_name, []}) do
# {field, validation_name}
# end
# defp validation_info(field, {validation_name, opts}) do
# {field, validation_name, filter_options(opts)}
# end
# defp filter_options([{_, _} | _] = opts), do: opts |> Keyword.drop([:count]) |> Enum.sort
# defp filter_options(opts), do: Enum.sort(opts)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment