View Source Usage
This document details further how Strukt is implemented, and how to use it. Please report any issues on the issue tracker.
struct-definition
Struct Definition
There are two variants, depending on how you want to define your structs, Strukt.defstruct/1
and Strukt.defstruct/2
.
The first is used to define a struct associated with the current module being defined:
defmodule Person do
use Strukt
defstruct do
field :name, :string, required: true
end
def name(person), do: person.name
end
The second is used to define a struct and its module, inline:
defmodule Entities do
use Strukt
defstruct Person do
field :name, :string, required: true
def name(person), do: person.name
end
end
The latter is generally useful only when you want to define multiple modules in the same file, which is probably relatively rare, but comes up from time to time, and this reduces the boilerplate a bit.
Lastly, it is worth noting that embedded structs behave almost identically to Strukt.defstruct/2
:
defmodule Company do
use Strukt
defstruct do
field :name, :string, required: true
embeds_many :employees, Employee do
field :name, :string, required: true
field :email, :string, required: true, format: ~r/^.+@.+$/
def name(employee), do: employee.name
end
end
end
In the above, you'd end up with two modules, Company
and Company.Employee
. It's generally recommended to
split up the definition of embedded structs, but in simple cases where the embedded type is strictly used only
within the context of the containing type, it may be easier to keep the definitions together like this.
working-with-structs
Working with Structs
The typical usage pattern for structs defined with Strukt more or less falls into one of the following buckets:
- Create a new struct, using the generated
new/1
function, which returns{:ok, struct}
or{:error, changeset}
- Given a struct, and a set of changes, apply them to the struct using
change/2
, producing anEcto.Changeset
- Given an
Ecto.Changeset
representing the struct, get back the struct usingfrom_changeset/1
, which likenew/1
, returns{:ok, struct}
or{:error, changeset}
Both new/1
and change/2
build on a common changeset function that performs casts for fields and embeds, and
runs all of the validation rules, including custom ones defined in validate/1
. The primary difference between
the two is that new/1
also performs autogeneration for fields (if applicable), and automatically invokes from_changeset/1
to get back the struct value.
If you need to do custom initialization of your own, then you can override new/1
yourself, making sure that you
invoke super(params)
at some point to perform all of the standard initialization logic. For example, if you wanted
to generate a primary key that is based on a hash of the contents of some fields of the struct, you might do something
like this:
defmodule Thing do
use Strukt
defstruct do
# This overrides the default primary key to disable autogeneration
field :uuid, Ecto.UUID, primary_key: true
field :name, :string
field :email, string
end
def new(params \\ %{})
def new(params) do
with {:ok, thing} <- super(params) do
hash =
:crypto.hash_init(:sha256)
|> :crypto.hash_update(thing.name)
|> :crypto.hash_update(thing.email)
|> :crypto.hash_final()
|> Base.encode32()
{:ok, %__MODULE__{thing | uuid: UUID.uuid5(:oid, hash, :default)}}
end
end
end
more-information
More Information
You may also find the Schemas and JSON documents useful for answering more specific questions about those features.