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 an Ecto.Changeset
  • Given an Ecto.Changeset representing the struct, get back the struct using from_changeset/1, which like new/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.