Skip to content

Commit 53538da

Browse files
committed
docs: add Type only on ecto schema guide
1 parent 4767e62 commit 53538da

File tree

2 files changed

+109
-0
lines changed

2 files changed

+109
-0
lines changed
+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Type Only on Ecto Schema
2+
3+
`Ecto` is a great library for working with both databases and data validation.
4+
However, it has its own way of defining schemas and fields,
5+
which results a struct but without type definitions.
6+
This plugin automatically disables struct creation for `Ecto` schemas.
7+
8+
## Implementation
9+
10+
It uses the `c:TypedStructor.Plugin.before_definition/2` callback to determine if the module is an `Ecto` schema
11+
by checking the `@ecto_fields` module attribute. If it is, the `:define_struct` option is
12+
set to `false` to prevent struct creation.
13+
14+
Here is the plugin(*feel free to copy and paste*):
15+
```elixir
16+
defmodule MyApp.TypedStructor.Plugins.TypeOnlyOnEctoSchema do
17+
use TypedStructor.Plugin
18+
19+
@impl TypedStructor.Plugin
20+
defmacro before_definition(definition, _opts) do
21+
quote do
22+
if Module.has_attribute?(__MODULE__, :ecto_fields) do
23+
Map.update!(unquote(definition), :options, fn opts ->
24+
Keyword.put(opts, :define_struct, false)
25+
end)
26+
else
27+
unquote(definition)
28+
end
29+
end
30+
end
31+
end
32+
```
33+
34+
## Usage
35+
36+
To use this plugin, you can add it to the `typed_structor` block like this:
37+
```elixir
38+
defmodule MyApp.User do
39+
use TypedStructor
40+
use Ecto.Schema
41+
42+
typed_structor do
43+
plugin MyApp.TypedStructor.Plugins.TypeOnlyOnEctoSchema
44+
45+
field :id, integer(), enforce: true
46+
field :name, String.t()
47+
field :age, integer(), enforce: true # There is always a non-nil value
48+
end
49+
50+
schema "source" do
51+
field :name, :string
52+
field :age, :integer, default: 20
53+
end
54+
end
55+
```
56+
57+
## Registering the plugin globally
58+
```elixir
59+
config :typed_structor, plugins: [MyApp.TypedStructor.Plugins.TypeOnlyOnEctoSchema]
60+
```
61+
62+
Note that the plugin is applied to **all modules** that use `TypedStructor`,
63+
you can opt-out by determining the module name or other conditions.
64+
65+
Let's change the plugin to only apply to modules from the `MyApp` namespace(*feel free to copy and paste*):
66+
67+
```elixir
68+
defmodule MyApp.TypedStructor.Plugins.TypeOnlyOnEctoSchema do
69+
use TypedStructor.Plugin
70+
71+
@impl TypedStructor.Plugin
72+
defmacro before_definition(definition, _opts) do
73+
quote do
74+
# Check if the module is from the MyApp namespace
75+
with "MyApp" <- __MODULE__ |> Module.split() |> hd(),
76+
true <- Module.has_attribute?(__MODULE__, :ecto_fields) do
77+
Map.update!(unquote(definition), :options, fn opts ->
78+
Keyword.put(opts, :define_struct, false)
79+
end)
80+
else
81+
_otherwise -> unquote(definition)
82+
end
83+
end
84+
end
85+
end
86+
```
87+
88+
Now you can use `typed_structor` without registering the plugin explicitly:
89+
90+
```diff
91+
defmodule MyApp.User do
92+
use TypedStructor
93+
use Ecto.Schema
94+
95+
typed_structor do
96+
- plugin MyApp.TypedStructor.Plugins.TypeOnlyOnEctoSchema
97+
98+
field :id, integer(), enforce: true
99+
field :name, String.t()
100+
field :age, integer(), enforce: true
101+
end
102+
103+
schema "source" do
104+
field :name, :string
105+
field :age, :integer, default: 20
106+
end
107+
end
108+
```

mix.exs

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ defmodule TypedStructor.MixProject do
2222
extras: [
2323
"README.md",
2424
"guides/migrate_from_typed_struct.md",
25+
"guides/plugins/type_only_on_ecto_schema.md",
2526
"CHANGELOG.md"
2627
]
2728
],

0 commit comments

Comments
 (0)