@pithyless good point about composition of things. Not sure myself what is a best practise with everything. Thinking aloud the ways to do things and when they should (or not) be used:
Advanced usage:
1. implementing IntoSchema
is the last effort in doing things, e.g. adding a new :regal/regex
type, which has custom explain, transform etc.
2. using -simple-schema
is helper for 1 (but only for leaf-schemas).
Basic usage:
using schemas as data should be the common way of doing & composing things. One can set transformation rules, humanized error messages, json schema mappings etc. as schema properties. Two options for composing things:
1. Vars. just say (def Over6 (m/schema [:int {:min 6, :description "should be over 6}]))
and use the Var. Caveat: inlines the forms: (m/form [:and int? Over6]) ; => [:and int? [:int {:min 6, :description "should be over 6"}]]
, making large schema hard to read
2. Via registry:, e.g. {"Over6" [:int {:min 6}]}
, keeps the reference visible: (m/form [:and int? "Over6"]) ; => [:and int? "Over6"]
keeping the schema forms clean too
added support for m/type-properties
-based transformations too. And a way for -m/simple-schema
to create the Schema Instances based on actual Schema instance properties.
Not sure how useful this is, but as the malli lifecycle already supports this, just made it easy to use:
(testing "with instance-based type-properties"
(let [Over (m/-simple-schema
(fn [{:keys [value]} _]
(assert (int? value))
{:type :user/over
:pred #(and (int? %) (> % value))
:type-properties {:error/message (str "should be over " value)
:decode/string mt/-string->long
:json-schema/type "integer"
:json-schema/format "int64"
:json-schema/minimum value}}))]
(testing "over6"
(let [schema [Over {:value 6}]]
(testing "form"
(is (= [:user/over {:value 6}] (m/form schema))))
(testing "validation"
(is (false? (m/validate schema 6)))
(is (true? (m/validate schema 7))))
(testing "properties"
(is (= {:error/message "should be over 6"
:decode/string mt/-string->long
:json-schema/type "integer"
:json-schema/format "int64"
:json-schema/minimum 6}
(m/type-properties schema)))
(is (= {:value 6}
(m/properties schema))))))
(testing "over42"
(let [schema [Over {:value 42}]]
(testing "form"
(is (= [:user/over {:value 42}] (m/form schema))))
(testing "validation"
(is (false? (m/validate schema 42)))
(is (true? (m/validate schema 43))))
(testing "properties"
(is (= {:error/message "should be over 42"
:decode/string mt/-string->long
:json-schema/type "integer"
:json-schema/format "int64"
:json-schema/minimum 42}
(m/type-properties schema)))
(is (= {:value 42}
(m/properties schema))))))))
🍺 to the first who finds a valid use case for this.
so m/-simple-schema
taks either a props map or a function of properties children => props
so the props (including :type-properties
) can be derived from schema properties.
I’m trying out Malli for the first time:
(def schema [:map-of int? uuid?])
(def m {"0" "2ac307dc-4ec8-4046-9b7e-57716b7ecfd2"
"1" "820e5003-6fff-480b-9e2b-ec3cdc5d2f78"})
(m/decode schema m mt/json-transformer)
user=> {"0" #uuid "2ac307dc-4ec8-4046-9b7e-57716b7ecfd2"
"1" #uuid "820e5003-6fff-480b-9e2b-ec3cdc5d2f78"}
I expected that this code would convert the keys to ints, can I modify the code in some way to make that happen?hi @schmee. The mt/json-transformer
doesn’t transform ints from strings as ints can be presented in JSON. But - actually, this only applies to values, so I think the keys should still be converted (as all keys are strings in JSON. Could you write an issue out of this?
to make it work today, you can use mt/string-transformer
which covers that out-of-the-box.
perfect, thanks! I’ll make an issue :thumbsup:
re-wrote m/-predicate-schema
, `m/-partial-predicate-schema` and `m/-leaf-schema` using m/-simple-schema
. -39 loc.
feel free to rename the issue to something that makes more sense to you!
Actually, the fix was simple, we already had :map-of
type transformation. Just needed add a optional argument to the mt/json-transformer
that is the string-decoders, which are used for :map-of
keys. The full impl looks like:
and now:
(deftest map-of-json-keys-transform
(let [schema [:map-of int? uuid?]]
(doseq [data [{:0 "2ac307dc-4ec8-4046-9b7e-57716b7ecfd2"
:1 "820e5003-6fff-480b-9e2b-ec3cdc5d2f78"}
{"0" "2ac307dc-4ec8-4046-9b7e-57716b7ecfd2"
"1" "820e5003-6fff-480b-9e2b-ec3cdc5d2f78"}]]
(is (= {0 #uuid"2ac307dc-4ec8-4046-9b7e-57716b7ecfd2"
1 #uuid"820e5003-6fff-480b-9e2b-ec3cdc5d2f78"}
(m/decode schema data mt/json-transformer))))))
@schmee fixed in master.
haha, that is one fast fix, thanks! 😄
looking forward to playing around more with Malli :thumbsup: