Skip to content

Commit b6d07e4

Browse files
committed
Update README
1 parent d58c222 commit b6d07e4

File tree

1 file changed

+53
-22
lines changed

1 file changed

+53
-22
lines changed

README.md

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,30 @@ conform!(%{user: %{name: "chris", age: -31}}, user_schema)
142142
(norm) lib/norm.ex:44: Norm.conform!/2
143143
```
144144

145+
Schema's are designed to allow systems to grow over time. They provide this
146+
functionality in two ways. The first is that any unspecified fields in the input
147+
are passed through when conforming the input. The second is that all keys in a
148+
schema are optional. This means that all of these are valid:
149+
150+
```elixir
151+
user_schema = schema(%{
152+
name: spec(is_binary()),
153+
age: spec(is_integer()),
154+
})
155+
156+
conform!(%{}, user_schema)
157+
=> %{}
158+
conform!(%{age: 31}, user_schema)
159+
=> %{age: 31}
160+
conform!(%{foo: :foo, bar: :bar}, user_schema)
161+
=> %{foo: :foo, bar: :bar}
162+
```
163+
164+
If you're used to more restrictive systems for managing data these might seem
165+
like odd choices. We'll see how to specify required keys when we discuss Selections.
166+
167+
#### Structs
168+
145169
You can also create specs from structs:
146170

147171
```elixir
@@ -183,23 +207,12 @@ Schemas accomodate growth by disregarding any unspecified keys in the input map.
183207
This allows callers to start sending new data over time without coordination
184208
with the consuming function.
185209

186-
### Selections
210+
### Selections and optionality
187211

188-
You may have noticed that there's no way to specify optional keys in
189-
a schema. This may seem like an oversight but its actually an intentional
190-
design decision. Whether a key should be present in a schema is determined
191-
by the call site and not by the schema itself. For instance think about
192-
the assigns in a plug conn. When are the assigns optional? It depends on
193-
where you are in the pipeline.
194-
195-
Schemas also force all keys to match at all times. This is generally
196-
useful as it limits your ability to introduce errors. But it also limits
197-
schema growth and turns changes that should be non-breaking into breaking
198-
changes.
199-
200-
In order to support both of these scenarios Norm provides the
201-
`selection/2` function. `selection/2` allows you to specify exactly the
202-
keys you require from a schema at the place where you require them.
212+
We said that all of the fields in a schema are optional. In order to specify
213+
the keys that are required in a specific use case we can use a Selection. The
214+
Selections takes a schema and a list of keys - or keys to lists of keys - that
215+
must be present in the schema.
203216

204217
```elixir
205218
user_schema = schema(%{
@@ -211,13 +224,33 @@ user_schema = schema(%{
211224
just_age = selection(user_schema, [user: [:age]])
212225

213226
conform!(%{user: %{name: "chris", age: 31}}, just_age)
214-
=> %{user: %{age: 31}}
227+
=> %{user: %{age: 31, name: "chris"}}
215228

216-
# Selection also disregards unspecified keys
217-
conform!(%{user: %{name: "chris", age: 31, unspecified: nil}, other_stuff: :foo}, just_age)
218-
=> %{user: %{age: 31}}
229+
conform!(%{user: %{name: "chris"}}, just_age)
230+
** (Norm.MismatchError) Could not conform input:
231+
val: %{name: "chris"} in: :user/:age fails: :required
232+
(norm) lib/norm.ex:387: Norm.conform!/2
219233
```
220234

235+
If you need to mark all fields in a schema as required you can elide the list
236+
of keys like so:
237+
238+
```elixir
239+
user_schema = schema(%{
240+
user: schema(%{
241+
name: spec(is_binary()),
242+
age: spec(is_integer()),
243+
})
244+
})
245+
246+
# Require all fields recursively
247+
conform!(%{user: %{name: "chris", age: 31}}, selection(user_schema))
248+
```
249+
250+
Selections are an important tool because they give control over optionality
251+
back to the call site. This allows callers to determine what they actually need
252+
and makes schema's much more reusable.
253+
221254
### Patterns
222255

223256
Norm provides a way to specify alternative specs using the `alt/1`
@@ -363,9 +396,7 @@ working to make improvements.
363396
Norm is being actively worked on. Any contributions are very welcome. Here is a
364397
limited set of ideas that are coming soon.
365398

366-
- [ ] Support generators for other primitive types (floats, etc.)
367399
- [ ] More streamlined specification of keyword lists.
368-
- [ ] selections shouldn't need a path if you just want to match all the keys in the schema
369400
- [ ] Support "sets" of literal values
370401
- [ ] specs for functions and anonymous functions
371402
- [ ] easier way to do dispatch based on schema keys

0 commit comments

Comments
 (0)