Strukt
Strukt provides an extended defstruct
macro which builds on top of Ecto.Schema
and Ecto.Changeset
to remove the boilerplate of defining type specifications,
implementing validations, generating changesets from parameters, JSON serialization,
and support for autogenerated fields.
This builds on top of Ecto embedded schemas, so the same familiar syntax you use today to define schema'd types in Ecto, can now be used to define structs for general purpose usage.
The functionality provided by the defstruct
macro in this module is strictly a superset
of the functionality provided both by Kernel.defstruct/1
, as well as Ecto.Schema
. If
you import it in a scope where you use Kernel.defstruct/1
already, it will not interfere.
Likewise, the support for defining validation rules inline with usage of field/3
, embeds_one/3
,
etc., is strictly additive, and those additions are stripped from the AST before field/3
and friends ever see it.
Installation
def deps do
[
{:strukt, "~> 0.1"}
]
end
Example
The following is an example of using defstruct/1
to define a struct with types, autogenerated
primary key, and validation rules.
defmodule Person do
use Strukt
@derives [Jason.Encoder]
@primary_key {:uuid, Ecto.UUID, autogenerate: true}
@timestamps_opts [autogenerate: {NaiveDateTime, :utc_now, []}]
defstruct do
field :name, :string, required: true
field :email, :string, format: ~r/^.+@.+$/
timestamps()
end
end
And an example of how you would create and use this struct:
# Creating from params, with autogeneration of fields
iex> {:ok, person} = Person.new(name: "Paul", email: "bitwalker@example.com")
...> person
%Person{
uuid: "d420aa8a-9294-4977-8b00-bacf3789c702",
name: "Paul",
email: "bitwalker@example.com",
inserted_at: ~N[2021-06-08 22:21:23.490554],
updated_at: ~N[2021-06-08 22:21:23.490554]
}
# Validation (Create)
iex> {:error, %Ecto.Changeset{valid?: false, errors: errors}} = Person.new(email: "bitwalker@example.com")
...> errors
[name: {"can't be blank", [validation: :required]}]
# Validation (Update)
iex> {:ok, person} = Person.new(name: "Paul", email: "bitwalker@example.com")
...> {:error, %Ecto.Changeset{valid?: false, errors: errors}} = Person.change(person, email: "foo")
...> errors
[email: {"has invalid format", [validation: :format]}]
# JSON Serialization/Deserialization
...> person == person |> Jason.encode!() |> Person.from_json()
true
For more, see the usage docs