(ns metabase.util.malli.registry (:refer-clojure :exclude [declare def]) (:require #?@(:clj ([malli.experimental.time :as malli.time])) [malli.core :as mc] [malli.registry] [malli.util :as mut]) #?(:cljs (:require-macros [metabase.util.malli.registry]))) | |
(defonce ^:private cache (atom {})) | |
Get a cached value for You generally shouldn't use this outside of this namespace unless you have a really good reason to do so! Make sure you used namespaced keys if you are using it elsewhere. | (defn cached [k schema value-thunk] (or (get (get @cache k) schema) ; get-in is terribly inefficient (let [v (value-thunk)] (swap! cache assoc-in [k schema] v) v))) |
Fetch a cached [[mc/validator]] for | (defn validator [schema] (cached :validator schema #(mc/validator schema))) |
[[mc/validate]], but uses a cached validator from [[validator]]. | (defn validate [schema value] ((validator schema) value)) |
Fetch a cached [[mc/explainer]] for | (defn explainer [schema] (letfn [(make-explainer [] #_{:clj-kondo/ignore [:discouraged-var]} (let [validator* (mc/validator schema) explainer* (mc/explainer schema)] ;; for valid values, it's significantly faster to just call the validator. Let's optimize for the 99.9% ;; of calls whose values are valid. (fn schema-explainer [value] (when-not (validator* value) (explainer* value)))))] (cached :explainer schema make-explainer))) |
[[mc/explain]], but uses a cached explainer from [[explainer]]. | (defn explain [schema value] ((explainer schema) value)) |
(defonce ^:private registry* (atom (merge (mc/default-schemas) (mut/schemas) #?(:clj (malli.time/schemas))))) | |
(defonce ^:private registry (malli.registry/mutable-registry registry*)) | |
(malli.registry/set-default-registry! registry) | |
Register a spec with our Malli spec registry. | (defn register! [schema definition] (swap! registry* assoc schema definition) (reset! cache {}) nil) |
Get the Malli schema for | (defn schema [type] (malli.registry/schema registry type)) |
Add a TODO -- we should change | (defn -with-doc [schema docstring] (cond (and (vector? schema) (map? (second schema))) (let [[tag opts & args] schema] (into [tag (assoc opts :description docstring)] args)) (vector? schema) (let [[tag & args] schema] (into [tag {:description docstring}] args)) :else [:schema {:description docstring} schema])) |
Like [[clojure.spec.alpha/def]]; add a Malli schema to our registry. | #?(:clj (defmacro def ([type schema] `(register! ~type ~schema)) ([type docstring schema] `(metabase.util.malli.registry/def ~type (-with-doc ~schema ~docstring))))) |
Like [[mc/deref-all]] but preserves properties attached to a | (defn- deref-all-preserving-properties [schema] (letfn [(with-properties [schema properties] (-> schema (mc/-set-properties (merge (mc/properties schema) properties)))) (deref* [schema] (let [dereffed (-> schema mc/deref deref-all-preserving-properties) properties (mc/properties schema)] (cond-> dereffed (seq properties) (with-properties properties))))] (cond-> schema (mc/-ref-schema? schema) deref*))) |
For REPL/test/documentation generation usage: get the definition of a registered schema from the registry.
Recursively resolves the top-level schema (e.g. a I was going to use [[mc/deref-recursive]] here but it tosses out properties attached to | (defn resolve-schema [schema] (let [schema (-> schema mc/schema deref-all-preserving-properties)] (mc/walk schema (fn [schema _path children _options] (cond (= (mc/type schema) :ref) schema (mc/-ref-schema? schema) (deref-all-preserving-properties (mc/-set-children schema children)) :else (mc/-set-children schema children))) ;; not sure this option is really needed, but [[mc/deref-recursive]] sets it... turning it off doesn't ;; seem to make any of our tests fail so maybe I'm not capturing something {::mc/walk-schema-refs true}))) |