wingyplus
ZoiDefstruct - an extension for Zoi to generate Elixir struct
I just did a dirty hack after seeing Zoi on x.com a few hours ago.
Quick Introduction
The zoi_defstruct is a library to help you generate a struct from the Zoi object schema (over Zoi.object). Let’s see a small example below:
defmodule K8S.Deployment do
use ZoiDefstruct
@schema Zoi.object(%{
apiVersion: Zoi.string(),
metadata:
Zoi.object(%{
name: Zoi.string()
}),
spec:
Zoi.object(%{
replicas: Zoi.integer() |> Zoi.optional()
})
})
defstructure(@schema)
end
When you call a defstructure The macro generates a struct with enforced keys and typespec for you automatically. So in the IEx, you can see:
A struct:
iex(4)> K8S.Deployment.__struct__()
%K8S.Deployment{spec: nil, metadata: nil, apiVersion: nil}
A typespec declaration:
iex(1)> t K8S.Deployment
@type t() :: %{
spec: %{optional(:replicas) => integer()},
metadata: %{name: binary()},
apiVersion: binary()
}
ArgumentError if it has no required keys:
iex(5)> %K8S.Deployment{}
** (ArgumentError) the following keys must also be given when building struct K8S.Depl
oyment: [:spec, :metadata, :apiVersion]
(examples 0.1.0) expanding struct: K8S.Deployment.__struct__/1
iex:5: (file)
Since this is a quick hack, it has numerous edge cases that need to be addressed. Please feel free to provide feedback or suggestions. Opening an issue or pull request is very welcome.
I need to give special thanks to Zoi for making this awesome library!
Most Liked
charlieaten
Great work @wingyplus, this is some inspiring stuff.
One concern I had though with this approach is that having a defstruct-like DSL that looks like Kernel.defstruct but behaves differently can be confusing.
Your work inspired me to experiment with Sigil, a proof-of-concept DSL for defining nested structs and validating external data using Zoi:
defmodule Post do
use Sigil.Schema
schema do
field :title, Zoi.string()
field :image, Image do
field :url, Zoi.string()
field :blurhash, Zoi.string()
field :metadata, Metadata do
field :size, Zoi.integer() |> Zoi.optional()
field :format, Zoi.string() |> Zoi.optional()
end
end
end
end
I’m still iterating and may try more compact forms for types like:
field :age, :integer, coerce: true
This has been a great way to learn macros and DSL design, and aims to complement(and obviously heavily inspired by) Ecto. Whereas Ecto handles data mapping and persistence at the external data/application(and database) layer, validation libraries like zoi and the likes and thus Sigil aim to provide a layer that works great for things like wrapping external api’s into client libraries and doing validation and egornomic casting into elixir constructs.
Thanks again—I hope others find it interesting.
phcurado
awesome @wingyplus
You can leverage some of the helpers on the Zoi Struct module as @byu mentioned. I personally decided to not add any macros into the library (unless strictly necessary like infering type specs) but I added these helpers for anyone who wishes to extend it.
Thanks for using Zoi, awesome to see this
nulltree
I’m so happy libraries like Zoi seem to catch on and inspire extension (infant adoption steps, but still).
They complement my way of thinking really well!
Popular in Announcing
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #hex









