Liking cljdoc? Tell your friends :D

license codecov build deploy clojars cljdoc

Warning: This library in designing and prototyping phase

Modelizer

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

The main goals:

  • simple

  • fast

  • extensible

  • serializable

Quick Start Guide

Add the following dependency in your project:

;; project.clj or build.boot
[modelizer "RELEASE"]

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

Example

You can see a full example 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

Development

# Run REPL & connect from your IDE
$ make repl

Testing

# Run all tests
$ make test

# Run Clojure tests
$ make test-clj

# Run ClojureScript tests
$ make test-cljs

Deploy

# create a new git tag
# available types `patch`, `minor`, `major`, `minor-rc`, `major-rc`, `minor-release`, `major-release`
$ make patch

# push a new git tag
$ make release

Available commands

$ make help
help                           Show help
clean                          Clean
repl                           Run REPL
lint                           Run linter
test-cljs                      Run ClojureScript tests
test-clj                       Run Clojure tests
test                           Run tests
jar                            Build jar
install                        Install locally
deploy                         Deploy to repository
init                           Init first version
patch                          Increment patch version
minor                          Increment minor version
major                          Increment major version
minor-rc                       Increment minor-rc version
major-rc                       Increment major-rc version
minor-release                  Increment minor-release version
major-release                  Increment major-release version
release                        Release a new version

This library inspired by the following excellent libraries:

Can you improve this documentation?Edit on GitHub

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

× close