Exdantic.StructValidator (exdantic v0.0.2)
View SourceValidator that optionally returns struct instances and executes model validators.
This module extends the existing validation logic to support:
- Returning struct instances when a schema is defined with
define_struct: true
- Executing model validators after field validation succeeds
- Computing derived fields after model validation succeeds
The validation pipeline:
- Field validation (existing logic)
- Model validation (Phase 2)
- Computed field execution (Phase 3)
- Struct creation (Phase 1)
Phase 4 Enhancement: Anonymous Function Support
Enhanced to properly handle both named functions and generated anonymous functions in model validators and computed fields. The validator can now execute:
- Named function model validators:
{MyModule, :validate_something}
- Generated anonymous function validators:
{MyModule, :__generated_model_validator_123_456}
- Named function computed fields:
{field_name, %ComputedFieldMeta{function_name: :my_function}}
- Generated anonymous computed fields:
{field_name, %ComputedFieldMeta{function_name: :__generated_computed_field_name_123_456}}
All function types are handled uniformly through the same execution pipeline.
Summary
Functions
Validates data against a schema with full pipeline support including computed fields.
Functions
@spec validate_schema(module(), map(), [atom() | String.t() | integer()]) :: {:ok, map() | struct()} | {:error, [Exdantic.Error.t()]}
Validates data against a schema with full pipeline support including computed fields.
Enhanced Validation Pipeline
- Field Validation: Validates individual fields using existing logic
- Model Validation: Executes model validators in sequence (Phase 2)
- Computed Field Execution: Executes computed fields to derive additional data (Phase 3)
- Struct Creation: Optionally creates struct instance (Phase 1)
Parameters
schema_module
- The schema module to validate againstdata
- The data to validate (must be a map)path
- Current validation path for error reporting (defaults to[]
)
Returns
{:ok, validated_data}
- wherevalidated_data
includes computed fields and is a struct if the schema defines one, otherwise a map{:error, errors}
- list of validation errors
Model Validator Execution
Model validators are executed in the order they are declared in the schema. Note: The validators are stored in reverse order due to Elixir's accumulate attribute behavior, but they are reversed back during execution to maintain declaration order. If any model validator returns an error, execution stops and the error is returned. Model validators can transform data by returning modified data in the success case.
Computed Field Execution
Computed fields are executed after model validation succeeds. Each computed field function:
- Receives the validated data (including any transformations from model validators)
- Must return
{:ok, computed_value}
or{:error, reason}
- Has its return value validated against the declared field type
- Contributes to the final validated result
Examples
# Basic model validation with computed fields
defmodule UserSchema do
use Exdantic, define_struct: true
schema do
field :first_name, :string, required: true
field :last_name, :string, required: true
field :email, :string, required: true
model_validator :normalize_names
computed_field :full_name, :string, :generate_full_name
computed_field :email_domain, :string, :extract_email_domain
end
def normalize_names(validated_data) do
normalized = %{
validated_data |
first_name: String.trim(validated_data.first_name),
last_name: String.trim(validated_data.last_name)
}
{:ok, normalized}
end
def generate_full_name(validated_data) do
{:ok, "#{validated_data.first_name} #{validated_data.last_name}"}
end
def extract_email_domain(validated_data) do
{:ok, validated_data.email |> String.split("@") |> List.last()}
end
end
iex> UserSchema.validate(%{
...> first_name: " John ",
...> last_name: " Doe ",
...> email: "john@example.com"
...> })
{:ok, %UserSchema{
first_name: "John", # normalized by model validator
last_name: "Doe", # normalized by model validator
email: "john@example.com",
full_name: "John Doe", # computed field
email_domain: "example.com" # computed field
}}
Error Handling
Model validators can return errors in several formats:
{:error, "string message"}
- converted to validation error{:error, %Exdantic.Error{}}
- used directly- Exception during execution - caught and converted to validation error
Computed field errors are handled gracefully:
- Function execution errors are caught and converted to validation errors
- Type validation errors for computed values are reported with field context
- Computed field errors include the field path and computation function reference