Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 18 additions & 25 deletions lib/money/ecto/money_ecto_composite_type.ex
Original file line number Diff line number Diff line change
Expand Up @@ -189,44 +189,37 @@ if Code.ensure_loaded?(Ecto.Type) do
end

def cast(%{"currency" => currency, "amount" => amount}, params)
when (is_binary(currency) or is_atom(currency)) and is_integer(amount) do
case Money.new(currency, amount, params) do
%Money{} = money -> {:ok, money}
{:error, {exception, message}} -> {:error, exception: exception, message: message}
end
end

def cast(%{"currency" => currency, "amount" => amount}, params)
when (is_binary(currency) or is_atom(currency)) and is_binary(amount) do
case Money.new(currency, amount, params) do
%Money{} = money -> {:ok, money}
{:error, {exception, message}} -> {:error, exception: exception, message: message}
end
end

def cast(%{"currency" => currency, "amount" => %Decimal{} = amount}, params)
when is_binary(currency) or is_atom(currency) do
case Money.new(currency, amount, params) do
%Money{} = money -> {:ok, money}
{:error, {exception, message}} -> {:error, exception: exception, message: message}
end
when (is_binary(currency) or is_atom(currency)) and
(is_integer(amount) or is_binary(amount) or is_struct(amount, Decimal)) do
currency
|> Money.new(amount, params)
|> cast_result()
end

def cast(%{currency: currency, amount: amount}, params) do
cast(%{"currency" => currency, "amount" => amount}, params)
end

def cast(string, params) when is_binary(string) do
case Money.parse(string, params) do
{:error, {exception, message}} -> {:error, exception: exception, message: message}
money -> {:ok, money}
end
string
|> Money.parse(params)
|> cast_result()
end

def cast(_money, _params) do
:error
end

# Money returns `{:error, {module, message}}` for its own errors and
# `{:error, exception}` for errors from Localize, such as an invalid locale.
defp cast_result(%Money{} = money), do: {:ok, money}

defp cast_result({:error, {exception, message}}),
do: {:error, exception: exception, message: message}

defp cast_result({:error, exception}) when is_exception(exception),
do: {:error, exception: exception.__struct__, message: Exception.message(exception)}

# embed_as is set to :dump because if it is set to
# `:self` then `cast/2` will be called when loading. And
# since casting is locale-sensitive, the results may
Expand Down
21 changes: 21 additions & 0 deletions test/money_ecto_edge_case_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,27 @@ defmodule Money.Ecto.EdgeCaseTest do
assert Composite.cast(nil, @params) == {:ok, nil}
assert Composite.cast(42, @params) == :error
end

test "an invalid locale is a cast error for \"100 CHF\" and a CHF 100 map" do
params = Composite.init(locale: "!invalid!")

for type <- [Composite, Map],
input <- [
"100 CHF",
%{"currency" => "CHF", "amount" => "100"},
%{currency: :CHF, amount: "100"}
] do
assert {:error, exception: Localize.InvalidLocaleError, message: message} =
type.cast(input, params)

assert message =~ "!invalid!"
end
end

test "an unknown currency XYZ is still a cast error" do
assert {:error, exception: Money.UnknownCurrencyError, message: _} =
Composite.cast(%{"currency" => "XYZ", "amount" => "1"}, @params)
end
end

describe "composite type equality and embedding" do
Expand Down