diff --git a/lib/ecto/json.ex b/lib/ecto/json.ex index b28c5d462e..ac2a5b499a 100644 --- a/lib/ecto/json.ex +++ b/lib/ecto/json.ex @@ -1,48 +1,52 @@ -if Code.ensure_loaded?(Jason.Encoder) do - defimpl Jason.Encoder, for: Ecto.Association.NotLoaded do - def encode(%{__owner__: owner, __field__: field}, _) do - raise """ - cannot encode association #{inspect(field)} from #{inspect(owner)} to \ - JSON because the association was not loaded. +for encoder <- [Jason.Encoder, JSON.Encoder] do + module = Macro.inspect_atom(:literal, encoder) - You can either preload the association: + if Code.ensure_loaded?(encoder) do + defimpl encoder, for: Ecto.Association.NotLoaded do + def encode(%{__owner__: owner, __field__: field}, _) do + raise """ + cannot encode association #{inspect(field)} from #{inspect(owner)} to \ + JSON because the association was not loaded. - Repo.preload(#{inspect(owner)}, #{inspect(field)}) + You can either preload the association: - Or choose to not encode the association when converting the struct \ - to JSON by explicitly listing the JSON fields in your schema: + Repo.preload(#{inspect(owner)}, #{inspect(field)}) - defmodule #{inspect(owner)} do - # ... + Or choose to not encode the association when converting the struct \ + to JSON by explicitly listing the JSON fields in your schema: - @derive {Jason.Encoder, only: [:name, :title, ...]} - schema ... do + defmodule #{inspect(owner)} do + # ... - You can also use the :except option instead of :only if you would \ - prefer to skip some fields. - """ + @derive {#{unquote(module)}, only: [:name, :title, ...]} + schema ... do + + You can also use the :except option instead of :only if you would \ + prefer to skip some fields. + """ + end end - end - defimpl Jason.Encoder, for: Ecto.Schema.Metadata do - def encode(%{schema: schema}, _) do - raise """ - cannot encode metadata from the :__meta__ field for #{inspect(schema)} \ - to JSON. This metadata is used internally by Ecto and should never be \ - exposed externally. + defimpl encoder, for: Ecto.Schema.Metadata do + def encode(%{schema: schema}, _) do + raise """ + cannot encode metadata from the :__meta__ field for #{inspect(schema)} \ + to JSON. This metadata is used internally by Ecto and should never be \ + exposed externally. - You can either map the schemas to remove the :__meta__ field before \ - encoding or explicitly list the JSON fields in your schema: + You can either map the schemas to remove the :__meta__ field before \ + encoding or explicitly list the JSON fields in your schema: - defmodule #{inspect(schema)} do - # ... + defmodule #{inspect(schema)} do + # ... - @derive {Jason.Encoder, only: [:name, :title, ...]} - schema ... do + @derive {#{unquote(module)}, only: [:name, :title, ...]} + schema ... do - You can also use the :except option instead of :only if you would \ - prefer to skip some fields. - """ + You can also use the :except option instead of :only if you would \ + prefer to skip some fields. + """ + end end end end diff --git a/mix.lock b/mix.lock index 462cdbebf3..b85b92f4ef 100644 --- a/mix.lock +++ b/mix.lock @@ -1,5 +1,5 @@ %{ - "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, + "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, "ex_doc": {:hex, :ex_doc, "0.35.1", "de804c590d3df2d9d5b8aec77d758b00c814b356119b3d4455e4b8a8687aecaf", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2121c6402c8d44b05622677b761371a759143b958c6c19f6558ff64d0aed40df"}, "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, diff --git a/test/ecto/json_test.exs b/test/ecto/json_test.exs index 5459902923..3e0b079213 100644 --- a/test/ecto/json_test.exs +++ b/test/ecto/json_test.exs @@ -1,29 +1,40 @@ defmodule Ecto.JsonTest do use ExUnit.Case, async: true + @implementations [{Jason, Jason.Encoder}, {JSON, JSON.Encoder}] + + loaded_implementations = + for {_lib, encoder} = implementation <- @implementations, + Code.ensure_loaded?(encoder), + do: implementation + defmodule User do use Ecto.Schema - @derive Jason.Encoder + @derive Keyword.values(loaded_implementations) schema "users" do has_many :comments, Ecto.Comment end end - test "encodes decimal" do - decimal = Decimal.new("1.0") - assert Jason.encode!(decimal) == ~s("1.0") - end + for {json_library, _encoder} <- loaded_implementations do + describe to_string(json_library) do + test "encodes decimal" do + decimal = Decimal.new("1.0") + assert unquote(json_library).encode!(decimal) == ~s("1.0") + end - test "fails on association not loaded" do - assert_raise RuntimeError, - ~r/cannot encode association :comments from Ecto.JsonTest.User to JSON/, - fn -> Jason.encode!(%User{}.comments) end - end + test "fails on association not loaded" do + assert_raise RuntimeError, + ~r/cannot encode association :comments from Ecto.JsonTest.User to JSON/, + fn -> unquote(json_library).encode!(%User{}.comments) end + end - test "fails when encoding __meta__" do - assert_raise RuntimeError, - ~r/cannot encode metadata from the :__meta__ field for Ecto.JsonTest.User to JSON/, - fn -> Jason.encode!(%User{comments: []}) end + test "fails when encoding __meta__" do + assert_raise RuntimeError, + ~r/cannot encode metadata from the :__meta__ field for Ecto.JsonTest.User to JSON/, + fn -> unquote(json_library).encode!(%User{comments: []}) end + end + end end end