diff --git a/src/provisdom/spectomic/core.clj b/src/provisdom/spectomic/core.clj index 95fac45..6ed5388 100644 --- a/src/provisdom/spectomic/core.clj +++ b/src/provisdom/spectomic/core.clj @@ -146,6 +146,41 @@ :att [s {}] :att-and-schema s))))) +(defn merge-opt-keys + "Merges optional keys into requried keys (for specs which are created using `clojure.spec.alpha/keys`) using a spec's form/description" + [fspec] + (let [keymap (into {} (map (fn [pair] (vec pair)) (partition 2 (rest fspec))))] + (->> (cond-> {} + (contains? keymap :opt) + (assoc :req (into (keymap :req []) (keymap :opt))) + (contains? keymap :opt-un) + (assoc :req-un (into (keymap :req-un []) (keymap :opt-un)))) + (mapcat identity) + (cons 'clojure.spec.alpha/keys)))) + +(defn regenerate-spec + "Regenerates a spec with its optional keys merged into required keys (for specs which are created using `clojure.spec.alpha/keys`)" + [spec] + (let [fspec (s/form spec) + spec-keys-fn 'clojure.spec.alpha/keys] + (if (coll? fspec) + (if (= (first fspec) spec-keys-fn) + (merge-opt-keys fspec) + (map + (fn [elem] + (if (coll? elem) + (if (= (first elem) spec-keys-fn) + (merge-opt-keys elem) + elem) + elem)) + fspec)) + (throw (AssertionError. "Spec should be composite"))))) + +(defn with-map-keys + "Extract out all the keys of composite spec" + [spec] + (keys (sgen/generate (s/gen (eval (regenerate-spec spec)))))) + (defn datomic-schema ([specs] (datomic-schema specs nil)) ([specs {:keys [custom-type-resolver] diff --git a/test/provisdom/spectomic/core_test.clj b/test/provisdom/spectomic/core_test.clj index 408895a..17e6c93 100644 --- a/test/provisdom/spectomic/core_test.clj +++ b/test/provisdom/spectomic/core_test.clj @@ -201,4 +201,27 @@ (deftest datascript-schema-transaction-test (let [schema (spectomic/datascript-schema test-schema-specs)] (testing "able to transact Spectomic generated schema to DataScript" - (is (ds/create-conn schema))))) \ No newline at end of file + (is (ds/create-conn schema))))) + + +(s/def :book/name string?) +(s/def :book/description string?) +(s/def ::book (s/keys :req [:book/name] :opt [:book/description])) + +(deftest datomic-schema-composite-spec-test + (let [expected-output [{:db/valueType :db.type/string + :db/cardinality :db.cardinality/one + :db/ident :book/name} + {:db/ident :book/description + :db/valueType :db.type/string + :db/cardinality :db.cardinality/one}] + actual-output (spectomic/datomic-schema (spectomic/with-map-keys ::book))] + (is (= actual-output expected-output)))) + +(deftest datomic-schema-composite-spec-fail-test + (let [expected-output "Spec should be composite" + actual-output (try + (spectomic/datomic-schema (spectomic/with-map-keys :book/name)) + (catch AssertionError e (.getMessage e)))] + (is (= actual-output expected-output)))) +