06: spec-gen
If you're not familiar with clojure.spec, check out the spec guide on clojure.org. It's very well-written.
Our code:
1
(ns reifyhealth.specmonstah-tutorial.06
2
(:require [reifyhealth.specmonstah.core :as sm]
3
[clojure.spec.alpha :as s]
4
[clojure.spec.gen.alpha :as gen]
5
[reifyhealth.specmonstah.spec-gen :as sg]))
6
7
(s/def ::id (s/and pos-int? #(< % 100)))
8
(s/def ::not-empty-string (s/and string? not-empty #(< (count %) 10)))
9
10
(s/def ::username ::not-empty-string)
11
(s/def ::user (s/keys :req-un [::id ::username]))
12
13
(s/def ::name ::not-empty-string)
14
(s/def ::topic (s/keys :req-un [::id ::name ::owner-id]))
15
16
(s/def ::owner-id ::id)
17
(s/def ::topic-id ::id)
18
(s/def ::content ::not-empty-string)
19
(s/def ::post (s/keys :req-un [::id ::owner-id ::topic-id ::content]))
20
21
(def schema
22
{:user {:prefix :u
23
:spec ::user}
24
:topic {:prefix :t
25
:spec ::topic
26
:relations {:owner-id [:user :id]}}
27
:post {:prefix :p
28
:spec ::todo
29
:relations {:topic-id [:topic :id]}}})
30
31
(defn ex-01
32
[]
33
{:user (gen/generate (s/gen ::user))
34
:topic (gen/generate (s/gen ::topic))
35
:post (gen/generate (s/gen ::post))})
Copied!
First we define some specs (lines 7-19) to generate a little dummy data. Here's what the raw generated data looks like:
1
(ex-01) ;=>
2
{:user {:id 2, :username "G95seixU"},
3
:topic {:id 11, :name "stA9xO50w", :owner-id 1},
4
:post {:id 57, :owner-id 2, :topic-id 2, :content "937x"}}
Copied!
That's useful, but we can't insert that into a test database because the foreign keys wouldn't match the values they reference. The :topic's :owner-id, for example, is 1, where the :user's :id is 2.
We can use reifyhealth.specmonstah.spec-gen/ent-db-spec-gen to generate data and then assign the foreign keys:
1
(defn ex-02
2
[]
3
(:data (sg/ent-db-spec-gen {:schema schema} {:post [[1]]})))
4
5
(ex-02)
6
; =>
7
{:nodeset #{:t0 :topic :p0 :u0 :post :user},
8
:adj {:post #{:p0}, :p0 #{:t0}, :topic #{:t0}, :t0 #{:u0}, :user #{:u0}},
9
:in {:p0 #{:post}, :t0 #{:topic :p0}, :u0 #{:t0 :user}},
10
:attrs {:post {:type :ent-type},
11
:p0 {:type :ent,
12
:index 0,
13
:ent-type :post,
14
:query-term [1],
15
:loom.attr/edge-attrs {:t0 {:relation-attrs #{:topic-id}}},
16
:spec-gen {:id 2, :owner-id 7, :topic-id 16, :content "3IU"}},
17
:topic {:type :ent-type},
18
:t0 {:type :ent,
19
:index 0,
20
:ent-type :topic,
21
:query-term [:_],
22
:loom.attr/edge-attrs {:u0 {:relation-attrs #{:owner-id}}},
23
:spec-gen {:id 16, :name "FM4fcV3t", :owner-id 2}},
24
:user {:type :ent-type},
25
:u0 {:type :ent,
26
:index 0,
27
:ent-type :user,
28
:query-term [:_],
29
:spec-gen {:id 2, :username "xh"}}}}
Copied!
Oh wow, OK. That's a lot to look at. Let's step through it.
We're looking at the value for the ent db's :data key. This is the loom graph that we've looked at in earlier sections, the graph returned by add-ents that captures ents and their relationships. Under the :attrs key, you can see that each ent (:p0, :t0, and :u0) now has the attribute :spec-gen. Under :spec-gen is a map that's been generated using clojure.spec, except that the foreign keys have been updated to be correct.
Sometimes you want to view just the data that clojure.spec has generated; viewing the entire ent db is overwhelming. To make that easier, Specmonstah has the reifyhealth.specmonstah.core/attr-map function:
1
(defn ex-03
2
[]
3
(-> (sg/ent-db-spec-gen {:schema schema} {:todo [[1]]})
4
(sm/attr-map :spec-gen)))
5
6
(ex-03)
7
;; =>
8
{:p0 {:id 30, :owner-id 6, :topic-id 11, :content "03hK"}
9
:t0 {:id 11, :name "A4rq01NK", :owner-id 84}
10
:u0 {:id 84, :username "QN8J68"}}
Copied!
attr-map returns a map where the keys are ent names and the values are the value of the given node attribute (:spec-gen here) for each ent. There's a convenience function that combines sg/ent-db-spec-gen and sm/attr-map, sg/ent-db-spec-gen-attr:
1
(defn ex-04
2
[]
3
(sg/ent-db-spec-gen-attr {:schema schema} {:post [[1]]}))
4
5
(ex-04)
6
;; =>
7
{:p0 {:id 2, :owner-id 20, :topic-id 2, :content "573AAM1D"}
8
:t0 {:id 2, :name "6q7a4", :owner-id 2}
9
:u0 {:id 2, :username "h"}}
Copied!
Last modified 2yr ago
Copy link