Liking cljdoc? Tell your friends :D

license codecov modelizer-core build modelizer-core deploy modelizer-core

Attention: This library is currently under active development and still in alpha

modelizer/core

A fast data-driven Clojure(Script) library for describing data and metadata that helps ensure data quality and governance.

  • Simple data-driven syntax

  • Fast

  • Extensible

  • Serializable

Quick Start Guide

Add the following dependency in your project:

;; project.clj or build.boot
[modelizer/core "0.0.5"]

;; deps.edn
{:deps {modelizer {:mvn/version "0.0.5"}}}

Example

You can see a full example {example-your-app}[here].

(ns your.app
  (:require
    [modelizer.core :as m]
    [modelizer.generator :as gen]))

;;;;
;; Registry
;;;;

;; Available functions for working with registry:
;; - (m/get-schemas)
;; - (m/get-schemas name)
;; - (m/get-schema name)
;; - (m/get-schema name version)
;; - (m/register schema)
;; - (m/register-generator validator generator)
;; - (m/defschema [schema])
;; - (m/defschema [name form])
;; - (m/defschema [name doc form])
;; - (m/defschema [name doc meta form])
;; - (m/defschema [name version doc meta form])
;; - (m/defschema [name version status doc meta form])


;; Create schema

(m/defschema :example
  [:or :string? [:and :pos-int? [:not :zero?]]])
;; => #modelizer/schema{:name      :example
;;                      :form      [:or :string? [:and :pos-int? [:not :zero?]]]
;;                      :version   :schema.version/not-specified
;;                      :status    :schema.status/not-specified
;;                      :validator #object[...]
;;                      :generator #clojure.test.check.generators.Generator[...]}


;; Create schema with a docstring

(m/defschema :example
  "Example schema"
  [:or :string? [:and :pos-int? [:not :zero?]]])
;; => #modelizer/schema{:name      :example
;;                      :form      [:or :string? [:and :pos-int? [:not :zero?]]]
;;                      :doc       "Example schema"
;;                      :version   :schema.version/not-specified
;;                      :status    :schema.status/not-specified
;;                      :validator #object[...]
;;                      :generator #clojure.test.check.generators.Generator[...]}


;; Create schema with a metadata

(m/defschema :example
  "Example schema"
  {:added "0.0.2"}
  [:or :string? [:and :pos-int? [:not :zero?]]])
;; => #modelizer/schema{:name      :example
;;                      :meta      {:added "0.0.2"}
;;                      :form      [:or :string? [:and :pos-int? [:not :zero?]]]
;;                      :doc       "Example schema"
;;                      :version   :schema.version/not-specified
;;                      :status    :schema.status/not-specified
;;                      :validator #object[...]
;;                      :generator #clojure.test.check.generators.Generator[...]}


;; Create schema with a specified version

(m/defschema :example "v1"
  "Example schema"
  {:added "0.0.2"}
  [:or :string? [:and :pos-int? [:not :zero?]]])
;; => #modelizer/schema{:name      :example
;;                      :meta      {:added "0.0.2"}
;;                      :form      [:or :string? [:and :pos-int? [:not :zero?]]]
;;                      :doc       "Example schema"
;;                      :version   "v1"
;;                      :status    :schema.status/not-specified
;;                      :validator #object[...]
;;                      :generator #clojure.test.check.generators.Generator[...]}


;; Create schema with a specified status

(m/defschema :example "v2" :production
  "Example schema"
  {:added "0.0.2"}
  [:or :string? [:and :pos-int? [:not :zero?]]])
;; => #modelizer/schema{:name      :example
;;                      :meta      {:added "0.0.2"}
;;                      :form      [:or :string? [:and :pos-int? [:not :zero?]]]
;;                      :doc       "Example schema"
;;                      :version   "v2"
;;                      :status    :production
;;                      :validator #object[...]
;;                      :generator #clojure.test.check.generators.Generator[...]}


;; Create schema from a map

(m/defschema
  {:name    :example
   :doc     "Example schema"
   :version "v3"
   :status  :production
   :meta    {:added "0.0.2"}
   :form    [:or :string? [:and :pos-int? [:not :zero?]]]})
;; => #modelizer/schema{:name      :example
;;                      :meta      {:added "0.0.2"}
;;                      :form      [:or :string? [:and :pos-int? [:not :zero?]]]
;;                      :doc       "Example schema"
;;                      :version   "v3"
;;                      :status    :production
;;                      :validator #object[...]
;;                      :generator #clojure.test.check.generators.Generator[...]}


;; If necessary, use the function instead of the macro

(m/register
  {:name    :example
   :doc     "Example schema"
   :version "v4"
   :status  :production
   :meta    {:added "0.0.2"}
   :form    [:or :string? [:and :pos-int? [:not :zero?]]]})
;; => #modelizer/schema{:name      :example
;;                      :meta      {:added "0.0.2"}
;;                      :form      [:or :string? [:and :pos-int? [:not :zero?]]]
;;                      :doc       "Example schema"
;;                      :version   "v4"
;;                      :status    :production
;;                      :validator #object[...]
;;                      :generator #clojure.test.check.generators.Generator[...]}


;; Get all registered schemas

(m/get-schemas)
;; => {...}


;; Get all schema versions

(m/get-schemas :example)
;; => {"v4"                          #modelizer/schema{...}
;;     ...
;;     "v1"                          #modelizer/schema{:name      :example
;;                                                     :doc       "Example schema"
;;                                                     :version   "v1"
;;                                                     :status    :production
;;                                                     :meta      {:added "0.0.2"}
;;                                                     :form      [:or :string? [:and :pos-int? [:not :zero?]]]
;;                                                     :validator #object[...]
;;                                                     :generator #clojure.test.check.generators.Generator[...]}
;;
;;     :schema.version/not-specified #modelizer/schema{:meta      {:added "0.0.2"}
;;                                                     :name      :example
;;                                                     :form      [:or :string? [:and :pos-int? [:not :zero?]]]
;;                                                     :doc       "Example schema"
;;                                                     :version   :schema.version/not-specified
;;                                                     :status    :schema.status/not-specified
;;                                                     :validator #object[...]
;;                                                     :generator #clojure.test.check.generators.Generator[...]}}


;; Get schema default version
;; NOTE: You will soon be able to specify a strategy to get the required schema version

(m/get-schema :example)
;; => #modelizer/schema{:meta      {:added "0.0.2"}
;;                      :name      :example
;;                      :form      [:or :string? [:and :pos-int? [:not :zero?]]]
;;                      :doc       "Example schema"
;;                      :version   :schema.version/not-specified
;;                      :status    :schema.status/not-specified
;;                      :validator #object[...]
;;                      :generator #clojure.test.check.generators.Generator[...]}


;; Get specified schema version

(m/get-schema :example "v4")
;; => #modelizer/schema{:name      :example
;;                      :doc       "Example schema"
;;                      :version   "v4"
;;                      :status    :production
;;                      :meta      {:added "0.0.2"}
;;                      :form      [:or :string? [:and :pos-int? [:not :zero?]]]
;;                      :validator #object[...]
;;                      :generator #clojure.test.check.generators.Generator[...]}



;;;;
;; Schema
;;;;

;; Available functions for working with schemas:
;; - (m/get-name schema)
;; - (m/get-version schema)
;; - (m/get-status schema)
;; - (m/get-doc schema)
;; - (m/get-meta schema)
;; - (m/get-form schema)
;; - (m/unwrap schema)
;; - (m/get-validator schema)
;; - (m/validate schema data)
;; - (m/validate schema data meta)
;; - (m/get-generator schema)
;; - (m/generate schema)
;; - (m/generate schema opts)
;; - (m/sample schema)
;; - (m/sample schema opts)



;;;;
;; Schema validators
;;;;

;; Save schema into variable

(def example1 (m/get-schema :example "v4"))

(m/validate example1 "1")               ;; => true
(m/validate example1 1)                 ;; => true
(m/validate example1 -1)                ;; => false
(m/validate example1 0)                 ;; => false

;; Save schema validator into variable

(def valid1? (m/get-validator example1))

(valid1? "1")                           ;; => true
(valid1? 1)                             ;; => true
(valid1? -1)                            ;; => false
(valid1? 0)                             ;; => false


;; Use aliases and versions

(m/defschema :example "v5" :production
  "Example schema"
  {:added   "0.0.2"
   :require {:my-example [:example "v4"]}}
  :my-example)
;; => #modelizer/schema{:meta      {:added   "0.0.2"
;;                                  :require {:my-example [:example "v4"]}}
;;                      :name      :example
;;                      :status    :production
;;                      :form      :my-example
;;                      :version   "v5"
;;                      :doc       "Example schema"
;;                      :validator #object[...]
;;                      :generator #clojure.test.check.generators.Generator[...]}

(def example2 (m/get-schema :example "v5"))

(m/validate example2 "1")               ;; => true
(m/validate example2 1)                 ;; => true
(m/validate example2 -1)                ;; => false
(m/validate example2 0)                 ;; => false

(def valid2? (m/get-validator example2))

(valid2? "1")                           ;; => true
(valid2? 1)                             ;; => true
(valid2? -1)                            ;; => false
(valid2? 0)                             ;; => false



;;;;
;; Schema generators
;;;;

;; simple

(m/defschema :example "v6"
  "Example schema"
  {}
  :string?)

(def example3 (m/get-schema :example "v6"))

(m/generate example3)                   ;; => "W"
(m/generate example3 {:size 10})        ;; => "MIt"
(m/sample example3)                     ;; => ("" "g" "HK" "5" "y" "Q" "48L3" "wAk5Fz9" "W1l" "")
(m/sample example3 {:size 5})           ;; => ("" "9" "lE" "979" "UOi5")


;; :or

(m/defschema :example "v7"
  "Example schema"
  {}
  [:or :string? :pos-int?])

(def example4 (m/get-schema :example "v7"))

(m/generate example4)                   ;; => 2
(m/generate example4 {:size 10})        ;; => "hjE5W"
(m/sample example4)                     ;; => (1 1 2 2 3 3 4 "7PuTp" 1 "66Gb")
(m/sample example4 {:size 5})           ;; => (1 2 1 "16" "Av")


;; :and

(m/defschema :example "v8"
  "Example schema"
  {}
  [:and :pos-int? :even?])

(def example5 (m/get-schema :example "v8"))

(m/generate example5)                   ;; => 6
(m/generate example5 {:size 10})        ;; => 240
(m/sample example5)                     ;; => (4 2 2 52 2 10 58 4 14 8)
(m/sample example5 {:size 5})           ;; => (2 4 2 64 8)


;; :not

(m/defschema :example "v8"
  "Example schema"
  {}
  [:not :string?])

(def example6 (m/get-schema :example "v8"))

(m/generate example6)                   ;; => (:*D/w)
(m/generate example6 {:size 10})        ;; => (0.5 (\a 0.2568359375))
(m/sample example6)                     ;; => ({} [false] nil nil nil [] nil nil nil nil)
(m/sample example6 {:size 5})           ;; => (() nil nil #{false} [zS8/o8])


;; Use aliases and versions

(m/defschema :example "v9"
  "Example schema"
  {:require {:pos-int [:pos-int?]}}
  [:and :pos-int [:not :even?]])

(def example7 (m/get-schema :example "v9"))

(m/generate example7)                   ;; => 9
(m/generate example7 {:size 10})        ;; => 20453
(m/sample example7)                     ;; => (5 31 7 5 1 7 1 1 5 243)
(m/sample example7 {:size 5})           ;; => (5 23 1 3 15)



;;;;
;; Example with a custom validator and generator
;;;

;; Create a custom validator

(defn custom-validator
  ;; Default validator
  ([x]
   (custom-validator x false))

  ;; 2-arity validator uses for specified cases when the result depends on external metadata.
  ;; E.g. you can add strict mode or whatever you want
  ([x randomize?]
   (if-not randomize?
     (uuid? x)
     (rand-nth [false true]))))


;; Create a custom generator

(def custom-generator gen/gen-uuid?)


;; Create a schema with a specified generator (explicitly)

(m/defschema :custom "v1"
  "Example schema"
  {:generator custom-generator}
  #'custom-validator)

(def custom-example1 (m/get-schema :custom "v1"))
(m/validate custom-example1 (m/generate custom-example1)) ;; => true
(m/validate custom-example1 (m/generate custom-example1) true) ;; => random result true/false


;; Register global validator and generator

(m/register-generator custom-validator custom-generator)

;; Create a schema without a specified generator (implicitly)

(m/defschema :custom "v2"
  "Example schema"
  {}
  #'custom-validator)

(def custom-example2 (m/get-schema :custom "v2"))
(m/validate custom-example2 (m/generate custom-example2)) ;; => true
(m/validate custom-example2 (m/generate custom-example2) true) ;; => random result true/false



;; :enum

(m/defschema :example "v10"
  "Example schema"
  {}
  [:enum "Steve Jobs" "Bill Gates"])

(def example8 (m/get-schema :example "v10"))

(m/generate example8)                   ;; => => "Steve Jobs"
(m/sample example8 {:size 2})           ;; => ("Steve Jobs" "Bill Gates")
(m/validate example8 "Steve Jobs")      ;; => true
(m/validate example8 "Elon Musk")       ;; => false


;; :<

(def example9
  (m/defschema :example "v11"
    "Example schema"
    {}
    [:< 42]))

(m/sample example9)                     ;; => (-1 -1 -1 1 -4 -7 -1 -14 -2 -7)
(m/validate example9 41)                ;; => true
(m/validate example9 42)                ;; => false
(m/validate example9 43)                ;; => false


;; :<=

(def example10
  (m/defschema :example "v12"
    "Example schema"
    {}
    [:<= 42]))

(m/sample example10)                    ;; => (0 -1 0 -1 0 3 9 -9 -5 -52)
(m/validate example10 41)               ;; => true
(m/validate example10 42)               ;; => true
(m/validate example10 43)               ;; => false


;; :=

(def example11
  (m/defschema :example "v13"
    "Example schema"
    {}
    [:= 42]))

(m/sample example11)                    ;; => (42 42 42 42 42 42 42 42 42 42)
(m/validate example11 41)               ;; => false
(m/validate example11 42)               ;; => true
(m/validate example11 43)               ;; => false


;; :>

(def example12
  (m/defschema :example "v14"
    "Example schema"
    {}
    [:> 42]))

(m/sample example12)                    ;; => (43 44 43 46 43 44 43 51 57 66)
(m/validate example12 41)               ;; => false
(m/validate example12 42)               ;; => false
(m/validate example12 43)               ;; => true


;; :>=

(def example13
  (m/defschema :example "v15"
    "Example schema"
    {}
    [:>= 42]))

(m/sample example13)                    ;; => (42 42 42 43 43 45 45 51 50 45)
(m/validate example13 41)               ;; => false
(m/validate example13 42)               ;; => true
(m/validate example13 43)               ;; => true


;; :not=

(def example14
  (m/defschema :example "v15"
    "Example schema"
    {}
    [:not= 42]))

(m/sample example14)                    ;; => (0 0 0 0 -1 -1 10 0 -16 0)
(m/validate example14 41)               ;; => true
(m/validate example14 42)               ;; => false
(m/validate example14 43)               ;; => true



;; The creation of the generator based on the inferred data type.
;; Otherwise, uses `gen-any?` generator

(m/sample (m/schema {:name :sample, :form [:> 42]})) ;;=> (44 43 44 44 44 43 60 45 44 245)
(m/sample (m/schema {:name :sample, :form [:> 42.0]})) ;;=> (43.0 64.0 43.0 72.0 88.0 96.0 54.0 43.0 54.25 171.5)
(m/sample (m/schema {:name :sample, :form [:not= "42"]})) ;; => ("" "" "6" "" "3" "7" "" "N8nonW6" "" "K08IcFx")

Roadmap

TBD

This library inspired by the following excellent libraries:

License

Copyright © 2019-2020 Ilshat Sultanov

Can you improve this documentation?Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close