diff --git a/app/lib/firehose/accounts/user.ex b/app/lib/firehose/accounts/user.ex index 458f208..b28d144 100644 --- a/app/lib/firehose/accounts/user.ex +++ b/app/lib/firehose/accounts/user.ex @@ -2,6 +2,16 @@ defmodule Firehose.Accounts.User do use Ecto.Schema import Ecto.Changeset + # `redact: true` below makes Ecto derive an `Inspect` impl for us. We write + # that impl out by hand instead (see the `defimpl` after the schema): + # Elixir 1.20.4 emits the derived code entirely on the `schema` line, and + # OTP 27's coverage instrumentation turns that single line into invalid + # Erlang ("variable '_2' unsafe in 'case'"), which aborts `mix test --cover` + # for the whole app. Output and redaction are identical, and the redaction is + # pinned by test/firehose/accounts_test.exs. Drop this and go back to the + # derive when the Elixir/OTP pins no longer reproduce the crash. + @derive_inspect_for_redacted_fields false + schema "users" do field :email, :string field :password, :string, virtual: true, redact: true @@ -12,6 +22,27 @@ defmodule Firehose.Accounts.User do timestamps(type: :utc_datetime) end + defimpl Inspect do + alias Firehose.Accounts.User + + # Same set as the `redact: true` field options above; keep them in sync. + @redacted_fields [:password, :hashed_password] + + def inspect(%User{} = user, opts) do + infos = + for %{field: field} = info <- User.__info__(:struct), + field not in @redacted_fields, + do: info + + Inspect.Any.inspect_as_struct( + user, + Macro.inspect_atom(:literal, User), + infos, + opts + ) + end + end + @doc """ A user changeset for registering or changing the email.