Skip to content

Commit

Permalink
Dump to map if dump/2 is called for embedded_dump
Browse files Browse the repository at this point in the history
See [papertrail issue](izelnakri/paper_trail#230).

Papertrail calls `Ecto.embedded_dump/2` for all schemas, not
just embedded schemas. This commit detects if `dump/2` is called
from `Ecto.embedded_dump/2` and if so returns a map instead of
the usual tuple.
  • Loading branch information
kipcole9 committed Jan 19, 2024
1 parent 5d12589 commit 26bd476
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 3 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

**Note** That `money_sql` is supported on Elixir 1.11 and later only.

## Money_SQL v1.11.0

This is the changelog for Money_SQL v1.11.0 released on January 19th, 2023.

### Enhancements

* When dumping a `Money.Ecto.Composite.Type`, detect if `dump/3` is being called by `Ecto.Type.embedded_dump/2`. If it is, then return a map that can be serialized to JSON. If it isn't (most cases) then return the tuple to be serialized to Postgres. See [papertrail issue](https://github.com/izelnakri/paper_trail/issues/230).

## Money_SQL v1.10.2

This is the changelog for Money_SQL v1.10.2 released on December 13th, 2023.
Expand Down
35 changes: 33 additions & 2 deletions lib/money/ecto/money_ecto_composite_type.ex
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,12 @@ if Code.ensure_loaded?(Ecto.Type) do
@impl Ecto.ParameterizedType
def dump(money, dumper \\ nil, params \\ [])

def dump(%Money{} = money, _dumper, _params) do
{:ok, {to_string(money.currency), money.amount}}
def dump(%Money{} = money, dumper, _params) do
if embedded_dump(dumper) do
Money.Ecto.Map.Type.dump(money)
else
{:ok, {to_string(money.currency), money.amount}}
end
end

def dump(nil, _, _) do
Expand All @@ -76,6 +80,24 @@ if Code.ensure_loaded?(Ecto.Type) do
:error
end

# Detects if we are being called on behalf the embedded dumper.
# In this case, we want to produce a map that can be serialized
# to JSON. See [papertrail issue](https://github.com/izelnakri/paper_trail/issues/230)

defp embedded_dump(nil) do
false
end

defp embedded_dump(dumper) do
case Function.info(dumper, :name) do
{:name, :"-embedded_dump/3-fun-0-"} ->
Function.info(dumper, :module) == {:module, Ecto.Type}

_other ->
false
end
end

# Casting in changesets

def cast(money) do
Expand Down Expand Up @@ -145,6 +167,15 @@ if Code.ensure_loaded?(Ecto.Type) do
:error
end

# 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
# not be correct due to variations in the decimal and grouping
# separators for different locales. This is because when casting
# we don't know if the data is coming from user input (and therefore should
# be locale awware) or from some JSON serialization (in which
# case it should not be locale aware).

def embed_as(term), do: embed_as(term, [])

@impl Ecto.ParameterizedType
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule Money.Sql.Mixfile do
use Mix.Project

@version "1.10.2"
@version "1.11.0"

def project do
[
Expand Down
17 changes: 17 additions & 0 deletions test/money_ecto_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,23 @@ defmodule Money.Ecto.Test do
assert customer.revenue == Money.new(:EUR, "12345,67")
end)
end

test "dump a money struct with Ecto.embedded_dump/3" do
organization = %Organization{payroll: Money.new(:USD, "100.23")}
dumped = Ecto.embedded_dump(organization, :json)

assert dumped == %{
name: nil,
value: %{"amount" => "0", "currency" => "USD"},
payroll: %{"amount" => "100.23", "currency" => "USD"},
tax: nil,
revenue: %{"amount" => "0", "currency" => "AUD"},
employee_count: nil,
customers: [],
inserted_at: nil,
updated_at: nil
}
end
end

for ecto_type_module <- [Money.Ecto.Composite.Type, Money.Ecto.Map.Type] do
Expand Down

0 comments on commit 26bd476

Please sign in to comment.