Skip to content

Commit b373bb1

Browse files
committed
Added documentation about the models.
1 parent 312acb8 commit b373bb1

File tree

2 files changed

+294
-0
lines changed

2 files changed

+294
-0
lines changed

doc/model_anatomy.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
## Node hierarchy
2+
3+
In Minimallist, models are a node hierarchy where each node is a hashmap.
4+
5+
Each node have a `:type` attribute to indicate what they represent.
6+
The node types currently supported are:
7+
8+
```clojure
9+
#{:fn :enum
10+
:and :or
11+
:set-of :map-of :map :sequence-of :sequence
12+
:alt :cat :repeat
13+
:let :ref}
14+
```
15+
16+
## Model of the models
17+
18+
A data model is currently in progress to represent the data models.
19+
It is written using Minimallist and can be found in the source code, in the
20+
namespace [`minimallist.minimap`](../src/minimallist/minimap.cljc).
21+
22+
You can use Minimallist and this model to verify that your models are
23+
valid:
24+
25+
```clojure
26+
(require '[minimallist.core :as m])
27+
(require '[minimallist.minimap :as mm])
28+
29+
(m/valid? mm/minimap-model my-model)
30+
```
31+
32+
## What a model looks like
33+
34+
Example:
35+
36+
```clojure
37+
{:type :map,
38+
:entries [{:key :name
39+
:model {:type :fn, :fn string?}}
40+
{:key :age
41+
:model {:type :fn, :fn int?}}
42+
{:key :gender
43+
:optional true
44+
:model {:type :enum, :values #{:female :male :other}}}]}
45+
```
46+
47+
Models can be pretty verbose to write.
48+
Minimallist provides a small set of helper functions to express them
49+
in a more concise way (see the section [Model Builder](model_builder.md)).
50+
51+
## Local definitions
52+
53+
A model can contain local definitions using a `:let` node, which can be referenced
54+
from an inner model specified in its `:body` attribute.
55+
56+
Before using the `:let` node:
57+
58+
```clojure
59+
{:type :map,
60+
:entries [{:key :me,
61+
:model {:type :map,
62+
:entries [{:key :name,
63+
:model {:type :fn, :fn string?}}
64+
{:key :age,
65+
:model {:type :fn, :fn int?}}]}}
66+
{:key :best-friend,
67+
:model {:type :map,
68+
:entries [{:key :name,
69+
:model {:type :fn, :fn string?}}
70+
{:key :age,
71+
:model {:type :fn, :fn int?}}]}}]}
72+
```
73+
74+
After using the `:let` node:
75+
76+
```clojure
77+
{:type :let,
78+
:bindings {'person {:type :map,
79+
:entries [{:key :name,
80+
:model {:type :fn, :fn string?}}
81+
{:key :age,
82+
:model {:type :fn, :fn int?}}]}},
83+
:body {:type :map,
84+
:entries [{:key :me,
85+
:model {:type :ref, :key 'person}}
86+
{:key :best-friend,
87+
:model {:type :ref, :key 'person}}]}}
88+
```
89+
90+
Local definition are also the key to unlock recursive data models.
91+
92+
Example:
93+
94+
```clojure
95+
{:type :let,
96+
:bindings {'person {:type :map,
97+
:entries [{:key :name,
98+
:model {:type :fn, :fn string?}}
99+
{:key :friends,
100+
:model {:type :sequence-of,
101+
:elements-model {:type :ref, :key 'person},
102+
:coll-type :vector}}]}},
103+
:body {:type :ref, :key 'person}}
104+
```

doc/model_builder.md

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
## Introduction
2+
3+
There are many ways to create Minimallist's model.
4+
5+
The namespace `minimallist.helper` provides one way to do it via
6+
a small set of functions, hopefully a smaller way to write
7+
(and read) them:
8+
9+
```clojure
10+
(require '[minimallist.helper :as h])
11+
12+
(h/map [:me (h/map [:name (h/fn string?)]
13+
[:age (h/fn int?)])]
14+
[:best-friend (h/map [:name (h/fn string?)]
15+
[:age (h/fn int?)])])
16+
17+
(h/let ['person (h/map [:name (h/fn string?)]
18+
[:age (h/fn int?)])]
19+
(h/map [:me (h/ref 'person)]
20+
[:best-friend (h/ref 'person)]))
21+
22+
(h/let ['person (h/map [:name (h/fn string?)]
23+
[:friends (h/vector-of (h/ref 'person))])]
24+
(h/ref 'person))
25+
```
26+
27+
## Usage
28+
29+
The helper function are very simple to use and combine.
30+
They are also documented in the source code.
31+
32+
### Custom predicate
33+
34+
Useful for anything not directly supported by Minimallist.
35+
36+
```clojure
37+
;; A string
38+
(h/fn string?)
39+
40+
;; An odd number
41+
(-> (h/fn int?)
42+
(h/with-condition (h/fn odd?)))
43+
44+
;; An odd number between 300 and 309
45+
(require '[clojure.test.check.generators :as tcg])
46+
(-> (h/fn int?)
47+
(h/with-condition (h/and (h/fn #(<= 300 % 309))
48+
(h/fn odd?)))
49+
(h/with-test-check-gen (tcg/fmap (fn [n] (+ 300 n n))
50+
(tcg/choose 0 4))))
51+
```
52+
53+
`h/fn` is the interface between Minimallist and the rest of the world.
54+
Any integration with a 3rd party library should be based on this node.
55+
56+
### Fixed value
57+
58+
This data model represents a fixed value.
59+
60+
```clojure
61+
(h/val 7)
62+
63+
;; Can be used on any Clojure value.
64+
(let [initial-inventory #{:sword :shield}]
65+
(h/val initial-inventory))
66+
```
67+
68+
### One value out of a set
69+
70+
Represents a value which belongs to a set of pre-defined values.
71+
72+
```clojure
73+
(h/enum #{:water :fire :earth :wind})
74+
```
75+
76+
### And / Or
77+
78+
Data model used for validation using conjunction or disjunctions of
79+
other logical predicates.
80+
81+
```clojure
82+
(-> (h/fn int?)
83+
(h/with-condition (h/or (h/fn #(<= 0 % 9))
84+
(h/val 42))))
85+
```
86+
87+
Those models are not supported by Minimallist's generator. If you want to use it
88+
outside of a `:condition-model` field, you need to add your own generator to their nodes
89+
90+
```clojure
91+
(-> (h/or (h/fn #(<= 0 % 9))
92+
(h/val 42))
93+
(h/with-test-check-gen (tcg/one-of [(tcg/choose 0 9)
94+
(tcg/return 42)])))
95+
```
96+
97+
### Collections `-of`
98+
99+
`set-of`, `map-of`, `sequence-of` represent collections of items of the same model.
100+
101+
```clojure
102+
;; A set of keywords.
103+
(h/set-of (fn keyword?))
104+
105+
;; Persons by name.
106+
(h/map-of (fn/string?) (ref 'person))
107+
108+
;; Sequence of numbers, either in a list or in a vector.
109+
(h/sequence-of (fn/int?))
110+
```
111+
112+
`list-of` and `vector-of` are shortcuts to define at the same time a `:sequence-of` node
113+
with a `:coll-type` set to `:list` or `:vector`.
114+
115+
```clojure
116+
;; A list of numbers.
117+
(h/list-of (fn/int?))
118+
119+
;; A vector of numbers.
120+
(h/sequence-of (fn/int?))
121+
```
122+
123+
### Collections with entries
124+
125+
`map` and `tuple` represent collections where each item has its own model
126+
specified using entries.
127+
128+
During data parsing, map entries are identified via their key, while tuple entries are either
129+
identified via their key or their index in the sequence.
130+
131+
```clojure
132+
(h/map [:name (h/fn string?)]
133+
[:age (h/fn int?)]
134+
[:gender {:optional true} (h/fn any?)])
135+
136+
(h/tuple [:first (h/fn int?)]
137+
(h/fn string?))
138+
```
139+
140+
`h/list` and `h/vector` are shortcuts to define at the same time a `:sequence` node
141+
(i.e. a `h/tuple`) with a `:coll-type` set to `:list` or `:vector`.
142+
143+
```clojure
144+
;; A list containing an integer followed by a string.
145+
(h/list [:first (h/fn int?)]
146+
(h/fn string?))
147+
148+
;; A vector containing an integer followed by a string.
149+
(h/vector [:first (h/fn int?)]
150+
(h/fn string?))
151+
```
152+
153+
### Alt
154+
155+
If your model can be either `A` or `B`, use the `:alt` node using `h/alt`.
156+
157+
```clojure
158+
;; Entries will be identified using their index.
159+
(h/alt (h/fn nil?)
160+
(h/fn boolean?)
161+
(h/fn number?)
162+
(h/fn string?))
163+
164+
;; Entries will be identified using their key.
165+
(h/alt [:nil (h/fn nil?)]
166+
[:boolean (h/fn boolean?)]
167+
[:number (h/fn number?)]
168+
[:string (h/fn string?)])
169+
```
170+
171+
### Let / Ref
172+
173+
`h/let` creates a model where some local models are defined.
174+
`h/ref` refers to those local models.
175+
176+
- Any value can be used as a key.
177+
- Inner local models are shadowing outer local models when using `h/ref`.
178+
179+
```clojure
180+
;; Avoids repeating one-self
181+
(h/let ['person (h/map [:name (h/fn string?)]
182+
[:age (h/fn int?)])]
183+
(h/map [:me (h/ref 'person)]
184+
[:best-friend (h/ref 'person)]))
185+
186+
;; Allows definition of recursive data structures
187+
(h/let ['person (h/map [:name (h/fn string?)]
188+
[:friends (h/vector-of (h/ref 'person))])]
189+
(h/ref 'person))
190+
```

0 commit comments

Comments
 (0)