Estructura.Nested (estructura v1.13.0)

Copy Markdown View Source

The nested struct with helpers to easily describe it and produce validation, coercion, and generation helpers.

Summary

Functions

DSL helper to produce coerce callbacks. The syntax is kinda weird, but bear with it, please.

Declares the shape of the target nested map from a JSON Schema definition.

Declares the shape of the target nested map. the values might be

DSL helper to produce validate callbacks. The syntax is kinda weird, but bear with it, please.

Functions

coerce(opts)

(macro)

DSL helper to produce coerce callbacks. The syntax is kinda weird, but bear with it, please.

It’s known to produce warnings in credo, I’m working on it.

coerce do
  def data.age(age) when is_float(age), do: {:ok, age}
  def data.age(age) when is_integer(age), do: {:ok, 1.0 * age}
  def data.age(age) when is_binary(age), do: {:ok, String.to_float(age)}
  def data.age(age), do: {:error, "Could not cast #{inspect(age)} to float"}
end

init(values)

(macro)

json_schema(schema)

(macro)

Declares the shape of the target nested map from a JSON Schema definition.

Accepts either a decoded JSON Schema map (with string keys) or a raw JSON binary string. The schema is converted to an Estructura.Nested shape at compile time using Estructura.Nested.JsonSchema.to_shape/1.

Any "default" values found in the schema are automatically used as initial values (equivalent to calling init/1).

See Estructura.Nested.JsonSchema for the full type mapping reference.

Example

defmodule MyApi.Response do
  use Estructura.Nested

  json_schema %{
    "type" => "object",
    "properties" => %{
      "id" => %{"type" => "integer"},
      "name" => %{"type" => "string", "default" => "anonymous"},
      "address" => %{
        "type" => "object",
        "properties" => %{
          "city" => %{"type" => "string"},
          "zip" => %{"type" => "string"}
        }
      },
      "tags" => %{"type" => "array", "items" => %{"type" => "string"}},
      "status" => %{"type" => "string", "enum" => ["active", "inactive"]}
    },
    "required" => ["id", "name"]
  }
end

You can also load from a file:

json_schema File.read!("priv/schemas/response.json")

shape(opts)

(macro)

Declares the shape of the target nested map. the values might be:

  • :string | :integer or another simple type understood by StreamData

  • [type] to declare a list of elements of a single type
  • map to declare a nesting level; in such a case, the module with the FQN is created, carrying the struct of the same behaviour
  • mfa tuple pointing out to the generator for this value

Example

defmodule User do
  use Estructura.Nested
  shape %{
    name: :string,
    address: %{city: :string, street: %{name: [:string], house: :string}},
    data: %{age: :float}
  }
end

%User{}

would result in

%User{
  address: %User.Address{
    city: nil,
    street: %User.Address.Street{house: nil, name: []}
  },
  data: %User.Data{age: nil},
  name: nil
  }

validate(opts)

(macro)

DSL helper to produce validate callbacks. The syntax is kinda weird, but bear with it, please.

It’s known to produce warnings in credo, I’m working on it.

validate do
  def address.street.postal_code(<<?0, code::binary-size(4)>>),
    do: {:ok, code}
  def address.street.postal_code(code),
    do: {:error, "Not a postal code (#{inspect(code)})"}
end