malli

https://github.com/metosin/malli :malli:
katox 2020-07-02T08:02:41.021100Z

@jkent of course, a shorter key than variants I tried. Thank you, it works!

ikitommi 2020-07-02T10:50:34.022500Z

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

ikitommi 2020-07-02T10:50:47.022600Z

ikitommi 2020-07-02T10:53:20.023800Z

as the map keys can be anything, the registry keys can be any non-vector. Strings looks nice.

ikitommi 2020-07-02T10:58:27.027600Z

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

ikitommi 2020-07-02T11:00:10.029400Z

clojure.spec with mutable global registry, fail late and mutable refs doesn’t seem that right anymore.

kwrooijen 2020-07-02T19:50:45.034300Z

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.

kwrooijen 2020-07-02T19:52:31.034700Z

I guess it's more that I find it very unexpected that the second example doesn't work