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:

(ns reifyhealth.specmonstah-tutorial.06
  (:require [reifyhealth.specmonstah.core :as sm]
            [clojure.spec.alpha :as s]
            [clojure.spec.gen.alpha :as gen]
            [reifyhealth.specmonstah.spec-gen :as sg]))

(s/def ::id (s/and pos-int? #(< % 100)))
(s/def ::not-empty-string (s/and string? not-empty #(< (count %) 10)))

(s/def ::username ::not-empty-string)
(s/def ::user (s/keys :req-un [::id ::username]))

(s/def ::name ::not-empty-string)
(s/def ::topic (s/keys :req-un [::id ::name ::owner-id]))

(s/def ::owner-id ::id)
(s/def ::topic-id ::id)
(s/def ::content ::not-empty-string)
(s/def ::post (s/keys :req-un [::id ::owner-id ::topic-id ::content]))

(def schema
  {:user  {:prefix :u
           :spec   ::user}
   :topic {:prefix    :t
           :spec      ::topic
           :relations {:owner-id [:user :id]}}
   :post  {:prefix    :p
           :spec      ::todo
           :relations {:topic-id [:topic :id]}}})

(defn ex-01
  []
  {:user  (gen/generate (s/gen ::user))
   :topic (gen/generate (s/gen ::topic))
   :post  (gen/generate (s/gen ::post))})

First we define some specs (lines 7-19) to generate a little dummy data. Here's what the raw generated data looks like:

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:

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:

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:

Last updated

Was this helpful?