@jkent of course, a shorter key than variants I tried. Thank you, it works!
Almost there:
(def Order
[:schema
{:registry {"Country" [:map
[:name [:enum :FI :PO]]
[:neighbors [:vector [:ref "Country"]]]]
"Burger" [:map
[:name string?]
[:description {:optional true} string?]
[:origin [:maybe "Country"]]
[:price pos-int?]]
"OrderLine" [:map
[:burger "Burger"]
[:amount int?]]
"Order" [:map
[:lines [:vector "OrderLine"]]
[:delivery [:map
[:delivered boolean?]
[:address [:map
[:street string?]
[:zip int?]
[:country "Country"]]]]]]}}
"Order"])
(malli.mermaid/class-diagram Order)
; classDiagram
; class Country {
; :name [:enum :FI :PO]
; :neighbors [:vector #:gen{:max 0} [:ref Country]]
; }
; class Burger {
; :name string?
; :description string?
; :origin [:maybe Country]
; :price pos-int?
; }
; class OrderLine {
; :burger Burger
; :amount int?
; }
; class Order {
; :lines [:vector OrderLine]
; :delivery Order_Delivery
; }
; class Order_Delivery_Address {
; :street string?
; :zip int?
; :country Country
; }
; class Order_Delivery {
; :delivered boolean?
; :address Order_Delivery_Address
; }
; Country o-- Country
; Burger o-- Country
; OrderLine o-- Burger
; Order o-- OrderLine
; Order *-- Order_Delivery
; Order_Delivery_Address o-- Country
; Order_Delivery *-- Order_Delivery_Address
as the map keys can be anything, the registry keys can be any non-vector. Strings looks nice.
when a Schema is created (using m/schema
), all the registry refs are eagerly validated and the current values are captured:
• have an Invalid ref? -> fail early
• using mutable registry? -> the created schema instance (including refs!) is still immutable
• wan’t to know the local registry which the schema was created with -> accessible via m/options
clojure.spec with mutable global registry, fail late and mutable refs doesn’t seem that right anymore.
Is this intended behavior?
Here we say that the :id
field is optional in the map schema. Which works
(m/validate
[:map
[:id {:optional true} int?]]
{}) ; => true
Here we give the :id
field a default value. But this doesn't work
(m/decode
[:map
[:id {:default 42} int?]]
{}
mt/default-value-transformer) ; => {}
Instead, we have to give field's spec a default, not the map's key
(m/decode
[:map
[:id [:and {:default 42} int?]]]
{}
mt/default-value-transformer) ; => {:id 42}
Personally I find the last example a bit strange (Or better yet, I find it strange that the second doesn't work). I'm not saying the ints have a default, I'm saying that this maps :id
field has a default. Just like how :id
is an optional field, not that that int?
is an optional schema.
Maybe there's a reason it's designed this way? I could just be looking at this from the wrong angle.I guess it's more that I find it very unexpected that the second example doesn't work