Splode.Error behaviour (splode v0.3.3)

Copy Markdown View Source

Use this module to create an aggregatable error.

For example:

defmodule MyApp.Errors.InvalidArgument do
  use Splode.Error, fields: [:name, :message], class: :invalid

  def message(%{name: name, message: message}) do
    "Invalid argument #{name}: #{message}"
  end
end

Message vars

After message/1 runs, Splode scans the rendered string for %{key} placeholders and replaces each with the corresponding value from the error's vars. vars are therefore an interpolation surface: everything in them is intended to be rendered into the message, so never place a value in vars that is sensitive or otherwise unsafe to display. vars are not a private side-channel for data you don't want in the message.

Because substitution runs against the already rendered string, it cannot distinguish a %{key} you wrote in the template from one that appeared because untrusted input was interpolated into the message. If you interpolate externally-controlled text into a message, that text can name any of the error's vars and have it substituted. This is only a concern when vars hold something you would not want rendered — which, per the rule above, they should not. Prefer interpolating untrusted values through vars ("...%{value}" with vars: [value: untrusted]) rather than splicing them directly into the template string.

Summary

Callbacks

Whether a keyword list should be treated as options for building this error.

Types

t()

@type t() :: Exception.t()

Callbacks

error_class?()

@callback error_class?() :: boolean()

from_json(map)

@callback from_json(map()) :: struct()

keyword_list_options?()

@callback keyword_list_options?() :: boolean()

Whether a keyword list should be treated as options for building this error.

Only consulted for the module configured as a Splode's :unknown_error. When it returns true — the default — a list that satisfies Keyword.keyword?/1 is destructured into :error, :vars and (if declared) :value, rather than being converted like any other term.

That is surprising for terms that happen to parse as keyword lists, such as OTP exit reasons:

Keyword.keyword?([{:noproc, {GenServer, :call, [:some_name, :ping, 5000]}}])
#=> true

Returning false opts out: keyword lists are then converted like any other list, so to_error(message: "it broke") produces two errors rather than one, and to_error([]) produces an empty error class rather than an unknown error. The default will change to false in the next major version.

defmodule MyApp.Errors.Unknown do
  use Splode.Error, fields: [:error], class: :unknown

  def keyword_list_options?, do: false
end

splode_error?()

@callback splode_error?() :: boolean()