Skip to content

metosin/schema-tools

Repository files navigation

Schema-tools Build status cljdoc badge

Common utilities for Prismatic Schema for Clojure/Script. Big sister to spec-tools.

  • common Schema definitions: any-keys, any-keyword-keys, open-schema, optional-keys-schema
  • schema-aware selectors: get-in, select-keys, select-schema
  • schema-aware transformers: assoc, dissoc, assoc-in, update-in, update, dissoc-in, merge, optional-keys, required-keys
    • removes the schema name and ns if the schema (value) has changed.
  • handle schema default values via default & default-coercion-matcher
  • meta-data helpers: schema-with-description schema-description, resolve-schema (clj only), resolve-schema-description (clj only)
  • coercion tools: or-matcher, map-filter-matcher, multi-matcher, coercer, coerce
  • tuned JSON & String matchers: from keywords, Java8 dates etc.
  • Protocol-based walker for manipulating Schemas in schema-tools.walk: walk, prewalk and postwalk.
  • Swagger2 generation
  • Coercion tools

API Docs.

Latest version

Clojars Project

Requires Java 1.8.

Examples

Normal clojure.core functions don't work well with Schemas:

(require '[schema.core :as s])

(s/defschema Address {:street s/Str
                      (s/optional-key :city) s/Str
                      (s/required-key :country) {:name s/Str}})

;; where's my city?
(select-keys Address [:street :city])
; {:street java.lang.String}

; this should not return the original Schema name...
(s/schema-name (select-keys Address [:street :city]))
; Address

With schema-tools:

(require '[schema-tools.core :as st])

(st/select-keys Address [:street :city])
; {:street java.lang.String, #schema.core.OptionalKey{:k :city} java.lang.String}

(s/schema-name (st/select-keys Address [:street :city]))
; nil

Coercion

If a given value can't be coerced to match a schema, ex-info is thrown (like schema.core/validate):

(require '[schema-tools.coerce :as stc])

(def matcher (constantly nil))
(def coercer (stc/coercer String matcher))

(coercer 123)
; clojure.lang.ExceptionInfo: Could not coerce value to schema: (not (instance? java.lang.String 123))
;      error: (not (instance? java.lang.String 123))
;     schema: java.lang.String
;       type: :schema-tools.coerce/error
;      value: 123

(coercer "123")
; "123"

; same behavior with coerce (creates coercer on each invocation, slower)
(stc/coerce 123 String matcher)
(stc/coerce "123" String matcher)

Coercion error :type can be overridden in both cases with an extra argument.

(stc/coerce 123 String matcher :domain/horror)
; clojure.lang.ExceptionInfo: Could not coerce value to schema: (not (instance? java.lang.String 123))
;      error: (not (instance? java.lang.String 123))
;     schema: java.lang.String
;       type: :domain/horror
;      value: 123

Select Schema

Filtering out illegal schema keys (using coercion):

(st/select-schema {:street "Keskustori 8"
                   :city "Tampere"
                   :description "Metosin HQ" ; disallowed-key
                   :country {:weather "-18" ; disallowed-key
                             :name "Finland"}}
                  Address)
; {:city "Tampere", :street "Keskustori 8", :country {:name "Finland"}}

Filtering out illegal schema map keys using coercion with additional Json-coercion - in a single sweep:

(s/defschema Beer {:beer (s/enum :ipa :apa)})

(def ipa {:beer "ipa" :taste "good"})

(st/select-schema ipa Beer)
; clojure.lang.ExceptionInfo: Could not coerce value to schema: {:beer (not (#{:ipa :apa} "ipa"))}
;     data: {:type :schema.core/error,
;            :schema {:beer {:vs #{:ipa :apa}}},
;            :value {:beer "ipa", :taste "good"},
;            :error {:beer (not (#{:ipa :apa} "ipa"))}}

(require '[schema.coerce :as sc])

(st/select-schema ipa Beer sc/json-coercion-matcher)
; {:beer :ipa}

Usage

See the tests.

License

Copyright © 2014-2019 Metosin Oy

Distributed under the Eclipse Public License 2.0.