This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-12-30
Channels
- # aleph (25)
- # announcements (20)
- # babashka (29)
- # babashka-sci-dev (12)
- # beginners (27)
- # biff (3)
- # clojure (29)
- # clojure-europe (21)
- # clojure-nl (1)
- # clojure-norway (1)
- # clojurescript (3)
- # clr (5)
- # code-reviews (4)
- # data-science (7)
- # datahike (6)
- # datascript (3)
- # emacs (9)
- # fulcro (5)
- # graalvm (10)
- # malli (15)
- # nbb (7)
- # off-topic (17)
- # pathom (9)
- # polylith (4)
- # practicalli (15)
- # reitit (3)
- # releases (2)
- # rum (1)
- # shadow-cljs (73)
- # squint (34)
- # tools-deps (3)
- # xtdb (11)
Hello, how should I achieve "use default value for a field when validation fails and the value is not compatible with the schema "? e.g.
(def MySchema
[:map
[:some-int-field {:default 30} :int]
[:some-boolean-field {:default false} :boolean]])
(m/decode
MySchema
{:some-int-field "typo" :some-boolean-field "another-typo"}
what-transformer-to-put-here--i-dont-know?)
The builtin default-value-transformer
would do nothing when the field is present but invalid.Good Question! There is no such thing atm but doable with the current transformers:
• on :leave
, validate the value, if not valid, replace with "the default". You can access the schemas (and the properties) in the transformation using the :compile
hook
code would be 90% identical to the default-value-transformer
, maybe could be just an option to it :thinking_face:
Awesome! I actually ended with something exactly the same as you suggested (got some inspiration from https://github.com/metosin/malli/issues/143 )
(def fix-with-default-transformer
(mt/transformer
{:name :fix-with-default
:default-decoder
{:compile
(fn [schema _]
(if-let [{:keys [default]} (m/properties schema)]
(fn [x]
(if (m/validate schema x)
x
default))
identity))}}))
(def my-transformers (mt/transformer
mt/string-transformer
fix-with-default-transformer))
(m/decode [:int {:default 100}] "99" my-transformers)
;; => 99
(m/decode [:int {:default 100}] "what a typo!" my-transformers)
;; => 100
One more thing I want is to be able to log the map field name that has this invalid value detected and replaced, e.g.
[:map
[:number-of-adults {:default 1} [:int {:min 1}]]
[:number-of-babies {:default 0} [:int {:min 0}]]]
and when I receive invalid input {:number-of-adults "foo"}
I'd like to log this down
[WARN] Got invalid input for :number-of-adults "foo", use default value '1' instead
But as I search around it looks like the path information is not available to the transformers. So I think the only way to achieve that is to use m/walk
to walk the schema and add the field name to the properties, e.g. the above schema would be enriched to be:
[:map
[:number-of-adults {:default 1 :report/name :number-of-adults} [:int {:min 1}]]
[:number-of-babies {:default 0 :report/name :number-of-babies} [:int {:min 0}]]]
This shall work, albeit being a bit verboseLooks good. Quick comments:
• it’s better to apply the transformation on :leave
so it’s postwalk. If you just return a function from :compile
, it maps to :enter
phase, so it’s a prewalk. If you use nested defaults, the top-level might fail on enter, as the children are not transformed.
• returning nil
from :compile
is better than identity
-> the transformation engine knows “there is nothing to do”
• you can create the (pure) validator just once, much faster
• so:
(defn fix-with-default-transformer []
(mt/transformer
{:name :fix-with-default
:default-decoder
{:compile
(fn [schema _]
(if-let [{:keys [default]} (m/properties schema)]
(let [valid? (m/validator schema)]
{:leave (fn [x] (if (valid? x) x default))})))}}))
• for the paths… what would you expect as a warning path from this:
(m/decode
[:map
[:x [:map
[:y [:int {:default 100}]]]]]
{:x {:y "what a typo!"}}
my-transformers)
;; => {:x {:y 100}}
Thanks! The validator tip is very useful. > for the paths… what would you expect as a warning path from this: I'll expect something like [:x :y] would be perfect.
Google pronounces it bit lazily, but close enough https://translate.google.fi/?hl=fi&sl=fi&tl=en&text=malli%0A&op=translate
No rush, I have a copy pasted namespace in the project now. But looking forward to a version bump for a few reasons 🙂
This is really good. Merged.
(is (= "one of <:dog = a map where {:x -> <integer>} | :cat = anything> dispatched by the type of animal"
(med/describe [:multi {:dispatch :type
:dispatch-description "the type of animal"}
[:dog [:map [:x :int]]]
[:cat :any]])))
(is (= "one of <:dog = a map where {:x -> <integer>} | :cat = anything> dispatched by :type"
(med/describe [:multi {:dispatch :type}
[:dog [:map [:x :int]]]
[:cat :any]])))