Run this notebook

Use Livebook to open this notebook and explore new ideas.

It is easy to get started, on your machine or the cloud.

Click below to open and run it in your Livebook at .

(or change your Livebook location)

<!-- livebook:{"app_settings":{"access_type":"public","auto_shutdown_ms":3600000,"multi_session":true,"show_source":true,"slug":"TypedStructCtor"}} --> # TypedStructCtor - providing constructors for structs ```elixir Mix.install([ {:typedstruct, "~> 0.5.2"}, {:typed_struct_ctor, "~> 0.1.0"}, {:typed_struct_ecto_changeset, "~> 1.0"} ]) ``` ## Use TypedStructCtor to add Constructors to your structs Use the [Constructor](https://medium.com/r/?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FConstructor_%28object-oriented_programming%29) functions (such as [new](https://hexdocs.pm/typed_struct_ctor/TypedStructCtor.html#new/1) and [new!](https://hexdocs.pm/typed_struct_ctor/TypedStructCtor.html#new!/1)) provided by the [TypedStructCtor](https://medium.com/r/?url=https%3A%2F%2Fhexdocs.pm%2Ftyped_struct_ctor%2FTypedStructCtor.html) macro to easily create validated structs. Using the [Domain Specific Language - DSL](https://en.wikipedia.org/wiki/Domain-specific_language) from the popular [TypedStruct](https://hexdocs.pm/typedstruct/readme.html) library you can easily create structs without the "noise" and duplication required by the [native syntax](https://hexdocs.pm/typed_struct_ctor/TypedStructCtor.html#new!/1). The TypedStructCtor plugin uses basic [Ecto.Changeset](https://hexdocs.pm/ecto/Ecto.Changeset.html#cast/4) enabled by the [TypedStructEctoChangeset plugin](https://hexdocs.pm/typed_struct_ecto_changeset/readme.html) to define basic Constructor functions that cast provided attributes, inject defaults for missing fields, and ensure required fields You can see for yourself in the code blocks below! Enter the code block, make changes and execute the block (the "play" button just above and to the right of the code block) <!-- livebook:{"reevaluate_automatically":true} --> ```elixir defmodule TLDR do use TypedStruct typedstruct do plugin(TypedStructEctoChangeset) plugin(TypedStructCtor) # A required field with a default value provided by MFA tuple returning a UUID field(:id, :string, default_apply: {Ecto.UUID, :generate, []}) # Or perhaps defaulted DateTime? field(:created_at, :utc_datetime_usec, default_apply: {DateTime, :utc_now, []}) # A required field with no default, meaning it must be provided to the constructor. # It's an `integer` with known `Ecto.cast` behavior so, in this instance, # string values are cast appropriately. e.g. "42" => 42 field(:an_integer, :integer) # An optional field with no default, meaning the new struct will # only have a value if provided as an attribute to the constructor field(:a_string, :string, required: false) end end ``` ## new() and new!() The`new/0`, `new!/0`, `new/1`, `new!/1` set of constructors take an optional map of attributes whose values will be cast to fields of like key-name in the struct under construction. For any field that has a default defined, it will be used if that field has no value in the attribute map. Finally, changeset will be validated and the struct returned ```elixir # The "Bang" version returns validated struct and will raise if changeset errors # Note that the string value in supplied attribute map is cast to an integer TLDR.new!(%{a_string: "bar", an_integer: "42"}) ``` ```elixir # A valid non-bang version returns {:ok, <struct>} TLDR.new(%{a_string: "bar", an_integer: "42"}) ``` ```elixir # Here we omit the attribute for required field ":an_integer" to show error changeset TLDR.new(%{a_string: "foo"}) ``` ```elixir # The "bang" version will `raise` if the Ecto.Changeset validation fails TLDR.new!(%{a_string: "foo"}) ``` ## from() and from!() for Event Driven systems The `from/1`, `from!/1`, `from/2` and `from!/2` take a required struct as the first parameter and an optional map of attributes. The "from" constructors are especially useful for event driven systems where it is common to create new Events derived from a set of fields from the Causal event and some set of additional attributess. Values from like-named fields will be copied from the originating struct to the struct being constructed (unless the field in the target struct is not mappable), any supplied attributes will be merged, defaults for missing fields will added, then Ecto.Changeset validations invoked. It is common, for example, that all such event structs have an "id" field unique to every instance, a "created_at" with a precision DateTime of when the event was constructed, a "correlation_id" that is copied from the "entry" event throughout the chain of related events, and sometimes a "causation_id" referencing the "id" of the event directly responsible for the given event. To facilitate this behavior, the `mappable?` field-level attribute specifies whether or not a given field should copy its value from the source struct which then allows the given field to be filled from a supplied function. By default all fields are `mappable?`, and can be overridden by field-level attributes when defining the struct <!-- livebook:{"reevaluate_automatically":true} --> ```elixir defmodule Event do use TypedStruct typedstruct do plugin(TypedStructEctoChangeset) plugin(TypedStructCtor) # Always generate a new ID field(:id, :string, mappable?: false, default_apply: {Ecto.UUID, :generate, []}) # Always use current time for newly created struct field(:created_at, :utc_datetime_usec, mappable?: false, default_apply: {DateTime, :utc_now, []} ) # By default all fields are `mappable?` so we'll copy this field from source struct field(:reason, :string) end end ``` The example(s) below demonstrate how the `from` constructors handle `mappable?: false` fields. Given the source (Causal) Event ```elixir source_struct = Event.new!(%{reason: "because"}) ``` A new event can be created from the Causal event with overriding attributes. Note the unique values for `id` and `created_at` ```elixir Event.from!(source_struct, %{reason: "I said so"}) ``` Here, the `id` field is provided in the attributes so will not be defaulted. ```elixir Event.from!(source_struct, %{id: "id from attributes"}) ```
See source

Have you already installed Livebook?

If you already installed Livebook, you can configure the default Livebook location where you want to open notebooks.
Livebook up Checking status We can't reach this Livebook (but we saved your preference anyway)
Run notebook

Not yet? Install Livebook in just a minute

Livebook is open source, free, and ready to run anywhere.

Run on your machine

with Livebook Desktop

Run in the cloud

on select platforms

To run on Linux, Docker, embedded devices, or Elixir’s Mix, check our README.

PLATINUM SPONSORS
SPONSORS
Code navigation with go to definition of modules and functions Read More ×