Skip to content

Commit bf55ec1

Browse files
committedJun 30, 2024··
docs: add Derives the Enumerable for struct guide
1 parent 786b4b4 commit bf55ec1

File tree

3 files changed

+68
-1
lines changed

3 files changed

+68
-1
lines changed
 

‎guides/plugins/derive_enumerable.md

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Derives the `Enumerable` for `struct`
2+
3+
We use the `c:TypedStructor.Plugin.after_definition/2` callback to
4+
generate the `Enumerable` implementation for the struct.
5+
We implement `Enumerable` callbacks exclusively using the fields that are defined.
6+
7+
Let's start!
8+
9+
## Implementation
10+
```elixir
11+
defmodule MyPlugin do
12+
use TypedStructor.Plugin
13+
14+
@impl TypedStructor.Plugin
15+
defmacro after_definition(definition, _opts) do
16+
quote bind_quoted: [definition: definition] do
17+
keys = Enum.map(definition.fields, &Keyword.fetch!(&1, :name))
18+
19+
defimpl Enumerable do
20+
def count(enumerable), do: {:ok, Enum.count(unquote(keys))}
21+
def member?(enumerable, element), do: {:ok, Enum.member?(unquote(keys), element)}
22+
23+
def reduce(enumerable, acc, fun) do
24+
# The order of fields is guaranteed to align with the sequence in which they are defined.
25+
unquote(keys)
26+
|> Enum.map(fn key -> {key, Map.fetch!(enumerable, key)} end)
27+
|> Enumerable.List.reduce(acc, fun)
28+
end
29+
30+
# We don't support this
31+
def slice(_enumerable), do: {:error, __MODULE__}
32+
end
33+
end
34+
end
35+
end
36+
```
37+
38+
## Usage
39+
```elixir
40+
defmodule User do
41+
use TypedStructor
42+
43+
typed_structor do
44+
plugin MyPlugin
45+
46+
field :name, String.t(), enforce: true
47+
field :age, integer(), enforce: true
48+
end
49+
end
50+
```
51+
52+
```elixir
53+
iex> user = %User{name: "Phil", age: 20}
54+
%User{name: "Phil", age: 20}
55+
# the order of fields is deterministic
56+
iex> Enum.map(user, fn {key, _value} -> key end)
57+
[:name, :age]
58+
# we got a deterministic ordered Keyword list
59+
iex> Enum.to_list(user)
60+
[name: "Phil", age: 20]
61+
```
62+
63+
> #### Bonus {: .info}
64+
> Additionally, we gain the bonus of having a deterministic order of fields,
65+
> determined by the sequence in which they are defined.

‎guides/plugins/introduction.md

+1
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ We provide some example plugins that you can use as a reference, or copy-paste.
1616
- [Type Only on Ecto Schema](./type_only_on_ecto_schema.md)
1717
- [Add primary key and timestamps types to your Ecto schema](./primary_key_and_timestamps.md)
1818
- [Derives the `Jason.Encoder` for `struct`](./derive_jason.md)
19+
- [Derives the `Enumerable` for `struct`](./derive_enumerable.md)

‎mix.exs

+2-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ defmodule TypedStructor.MixProject do
3535
"guides/plugins/registering_plugins_globally.md",
3636
"guides/plugins/type_only_on_ecto_schema.md",
3737
"guides/plugins/primary_key_and_timestamps.md",
38-
"guides/plugins/derive_jason.md"
38+
"guides/plugins/derive_jason.md",
39+
"guides/plugins/derive_enumerable.md"
3940
]
4041
],
4142
package: [

0 commit comments

Comments
 (0)
Please sign in to comment.