defmodule <%= app_module %>.DataCase do
  @moduledoc """
  This module defines the setup for tests requiring
  access to the application's data layer.

  You may define functions here to be used as helpers in
  your tests.

  Finally, if the test case interacts with the database,
  it cannot be async. For this reason, every test runs
  inside a transaction which is reset at the beginning
  of the test unless the test case is marked as async.
  """

  use ExUnit.CaseTemplate

  using do
    quote do
      alias <%= app_module %>.Repo

      import Ecto
      import Ecto.Changeset
      import Ecto.Query
      import <%= app_module %>.DataCase
    end
  end

  setup tags do
    <%= adapter_config[:test_setup] %>

    unless tags[:async] do
      <%= adapter_config[:test_async] %>
    end

    :ok
  end

  @doc """
  A helper that transforms changeset errors into a map of messages.

      assert {:error, changeset} = Accounts.create_user(%{password: "short"})
      assert "password is too short" in errors_on(changeset).password
      assert %{password: ["password is too short"]} = errors_on(changeset)

  """
  def errors_on(changeset) do
    Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
      Enum.reduce(opts, message, fn {key, value}, acc ->
        String.replace(acc, "%{#{key}}", to_string(value))
      end)
    end)
  end
end

defmodule <%= app_module %>.DataCase do
  @moduledoc """
  This module defines the setup for tests requiring
  access to the application's data layer.

  You may define functions here to be used as helpers in
  your tests.

  Finally, if the test case interacts with the database,
  it cannot be async. For this reason, every test runs
  inside a transaction which is reset at the beginning
  of the test unless the test case is marked as async.
  """

  use ExUnit.CaseTemplate

  using do
    quote do
      alias <%= app_module %>.Repo

      import Ecto
      import Ecto.Changeset
      import Ecto.Query
      import <%= app_module %>.DataCase
    end
  end

  setup tags do
    <%= adapter_config[:test_setup] %>

    unless tags[:async] do
      <%= adapter_config[:test_async] %>
    end

    :ok
  end

  @doc """
  A helper that transforms changeset errors into a map of messages.

      assert {:error, changeset} = Accounts.create_user(%{password: "short"})
      assert "password is too short" in errors_on(changeset).password
      assert %{password: ["password is too short"]} = errors_on(changeset)

  """
  def errors_on(changeset) do
    Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
      Enum.reduce(opts, message, &replace_message_embed/2)
    end)
  end

  defp replace_message_embed({_key, value}, message) when is_tuple(value), do: message

  defp replace_message_embed({key, value}, message) do
    String.replace(message, "%{#{key}}", to_string(value))
  end
end

Fix errors_on when using array field changeset
Fix errors_on when using array field changeset

## Overview
`errors_on/1` raises an error when it receives a changeset with an array
field which was `cast`-ed with an invalid type.

## Details
When validating the type of an array field fails, `cast/4` it will put an error on the changeset, such as:
```elixir
[my_list_field: {"is invalid", [type: {:array, :string}, validation: :cast]}]
```

While processing this error, `errors_on/1` will try to `to_string/1` the
`{:array, :string}` tuple, which produces the error:
```
** (Protocol.UndefinedError) protocol String.Chars not implemented for {:array, :string}. This protocol is implemented for: Atom, BitString, Date, DateTime, Decimal, Float, Integer, List, NaiveDateTime, Postgrex.Copy, Postgrex.Query, Time, URI, Version, Version.Requirement
```

## Solution
The implemented solution is to skip trying to interpolate error message embeds
when the value to be embedded is a tuple.

## Test Case
I didn't write a test for this, as I'm not sure how/where to do so for
installer templates. I tested locally with a setup like:
```elixir
defmodule MyAppWeb.Drzewo do
  use Ecto.Schema
  import Ecto.Changeset

  embedded_schema do
    field :kolory, {:array, :string}
  end

  def changeset(attrs) do
    cast(%Drzewo{}, attrs, [:kolory])
  end
end

defmodule MyAppWeb.DrzewoTest do
  use MyApp.DataCase

  alias MyAppWeb.Drzewo

  test "errors_on raises an error when passed an invalid type for the array field" do
    drzewo_params = %{kolory: 1}

    changeset = Drzewo.changeset(drzewo_params)
    changeset.valid?

    errors_on(changeset)
  end
end
```

jwarwick/phoenix,rafbgarcia/phoenix,michalmuskala/phoenix,Gazler/phoenix,nshafer/phoenix,0x00evil/phoenix,nshafer/phoenix,keathley/phoenix,eksperimental/phoenix,0x00evil/phoenix,phoenixframework/phoenix,nshafer/phoenix,jwarwick/phoenix,keathley/phoenix,phoenixframework/phoenix,rafbgarcia/phoenix,optikfluffel/phoenix,0x00evil/phoenix,optikfluffel/phoenix,optikfluffel/phoenix,Gazler/phoenix,eksperimental/phoenix,michalmuskala/phoenix,phoenixframework/phoenix,rafbgarcia/phoenix