JSON schema-based constraint specification.
Defines field types, required fields, enums, and validation rules.
Supported Types
:string- String values:integer- Integer values (supports:positiveconstraint):boolean- Boolean values (true/false):number- Numeric values (integer or float){:enum, values}- Enumerated values from a list{:array, item_spec}- Array/list of items with validation
Field Specification
Each field can have:
type- The field type (required)required- Whether the field must be present (default: false)description- Documentation for the field (optional)positive- For integers, must be > 0 (optional, default: false)min_length- For strings, minimum length in characters (optional)max_length- For strings, maximum length in characters (optional)min- For integers/numbers, minimum value (optional)max- For integers/numbers, maximum value (optional)min_items- For arrays, minimum number of items (optional)max_items- For arrays, maximum number of items (optional)unique_items- For arrays, whether items must be unique (default: false)pattern- For strings, custom regex pattern (Regex.t() or string)format- For strings, built-in format (:email, :url, :uuid, :phone, :date)
Example
address_schema = %ExOutlines.Spec.Schema{
fields: %{
city: %{type: :string, required: true},
zip_code: %{type: :string, required: true, min_length: 5}
}
}
schema = %ExOutlines.Spec.Schema{
fields: %{
name: %{type: :string, required: true, description: "User's full name"},
age: %{type: :integer, required: true, positive: true},
role: %{type: {:enum, ["admin", "user"]}, required: false},
active: %{type: :boolean, required: false},
tags: %{type: {:array, %{type: :string, max_length: 20}}, max_items: 10},
address: %{type: {:object, address_schema}, required: true}
}
}
# Valid input with nested object
ExOutlines.Spec.validate(schema, %{
"name" => "Alice",
"age" => 30,
"tags" => ["elixir"],
"address" => %{"city" => "NYC", "zip_code" => "10001"}
})
# => {:ok, %{name: "Alice", age: 30, tags: ["elixir"], address: %{city: "NYC", zip_code: "10001"}}}
# Invalid input (missing required field)
ExOutlines.Spec.validate(schema, %{"name" => "Bob"})
# => {:error, %Diagnostics{...}}
Summary
Functions
Add a field to an existing schema.
Create a new schema with field specifications.
Get required field names from the schema.
Types
@type field_spec() :: %{ type: field_type(), required: boolean(), description: String.t() | nil, positive: boolean(), min_length: non_neg_integer() | nil, max_length: pos_integer() | nil, min: number() | nil, max: number() | nil, min_items: non_neg_integer() | nil, max_items: pos_integer() | nil, unique_items: boolean(), pattern: Regex.t() | nil }
@type format() :: :email | :url | :uuid | :phone | :date
@type item_spec() :: %{ type: :string | :integer | :boolean | :number | {:enum, [any()]}, min_length: non_neg_integer() | nil, max_length: pos_integer() | nil, min: number() | nil, max: number() | nil }
@type t() :: %ExOutlines.Spec.Schema{fields: %{required(atom()) => field_spec()}}
Functions
@spec add_field(t(), atom(), field_type(), keyword()) :: t()
Add a field to an existing schema.
Examples
iex> schema = ExOutlines.Spec.Schema.new(%{})
iex> schema = ExOutlines.Spec.Schema.add_field(schema, :name, :string, required: true)
iex> Map.has_key?(schema.fields, :name)
true
Create a new schema with field specifications.
Examples
iex> schema = ExOutlines.Spec.Schema.new(%{
...> name: %{type: :string, required: true},
...> age: %{type: :integer, required: true, positive: true}
...> })
iex> is_struct(schema, ExOutlines.Spec.Schema)
true
Get required field names from the schema.
Examples
iex> schema = ExOutlines.Spec.Schema.new(%{
...> name: %{type: :string, required: true},
...> age: %{type: :integer, required: false}
...> })
iex> ExOutlines.Spec.Schema.required_fields(schema)
[:name]