malli

https://github.com/metosin/malli :malli:
ikitommi 2020-09-24T03:48:42.015900Z

@dcj there is poor man's documentation about that change in CHANGELOG. The m/children returns the [key properties schema] tuple3s, so just use that instead. The new m/entries (renamed iit so it's not a silent/evil breakage) is needed to effectively use the entry properties in schema applications like value transformation and schema transformations.

ikitommi 2020-09-24T04:02:40.022600Z

there is a long discussion about the options and the second in the related issue (https://github.com/metosin/malli/pull/212) and it's also documented in docstrings now: https://github.com/metosin/malli/blob/master/src/malli/core.cljc#L1127-L1155

1
Dave Simmons 2020-09-24T05:48:54.025100Z

Morning - I've been using Malli for the last few month. Last night when compiling my code lein pulled down a new version. I now get a failure when trying to compile my project Exception in thread "main" Syntax error compiling at (malli/core.cljc:1164:1).

Dave Simmons 2020-09-24T05:49:40.026Z

I pulled down an earlier version of my project but in case I'd done something daft but still get the same problem. Has anyone else experienced this issue? cheers.

ikitommi 2020-09-24T06:11:13.026700Z

@shortlyportly i can reproduce. the new dynaload works differently (better!), will fix that.

Dave Simmons 2020-09-24T06:21:33.027100Z

@ikitommi - awesome - many thanks.

ikitommi 2020-09-24T06:32:58.028200Z

@shortlyportly should be fixed in master & [metosin/malli "0.0.1-20200924.063109-27"], ping @sergey.tkachenko too.

ikitommi 2020-09-24T06:35:04.030600Z

also, sci is now loaded when it’s first used. Using preload (cljs) or direct require (cljs, graalvm, jvm) makes it eager. But: having sci on classpath and not needing it, means it wont get loaded by malli, yielding faster startup time:

(time (require '[malli.core :as m]))
"Elapsed time: 466.76624 msecs"
=> nil
(time (m/eval "(+ 1 1)"))
"Elapsed time: 1591.515317 msecs"
=> 2
(time (m/eval "(+ 1 1)"))
"Elapsed time: 0.587728 msecs"
=> 2
(time (m/eval "(+ 1 1)"))
"Elapsed time: 0.772357 msecs"
=> 2

ikitommi 2020-09-24T06:35:59.031600Z

when clj command line tooling 3rd party library aot caching works, this can be made eager again.

ikitommi 2020-09-24T06:36:41.031700Z

https://github.com/metosin/malli/pull/261

Dave Simmons 2020-09-24T07:01:20.032300Z

@ikitommi - thank you - seems to be back and working. cheers

2020-09-24T16:29:20.001Z

Next change, m/fn-schema went away? How should I change the below....

(def registry
  (merge (m/predicate-schemas)
         (m/class-schemas)
         (m/comparator-schemas)
         (m/base-schemas)
         {:zoned-date-time (m/fn-schema :zoned-date-time #'zoned-date-time?)
          :local-date      (m/fn-schema :local-date      #'local-date?)}))

ikitommi 2020-09-24T16:39:47.003400Z

@dcj there were 4 variants of the same thing, fn-schema, leaf-schema, predicate-schema, partial-predicate-schena , all replaced by m/-simple-schema. Also, there is way to reset the default registry now. But the schema:

(m/-simple-schema {:type :local-date, :pred #'local-date?})

ikitommi 2020-09-24T16:49:26.012600Z

it also give you a way to read the schema properties and use them for the validation or transformation:

(def Date
  (m/-simple-schema 
    (fn [{:keys [format type min]} children]
      ;; check props and children here, at schema instance creation time
      (let [string->date (create-formatter-somehow format)
            min-date (string->date min]
        {:type :local-date
         :pred (fn [x] (and (instance? type x) (check-that-is-greater-than min))
         :type-properties {:decode/string string->date
                           :error/message {:en "should be date"}
                           :gen/gen generator-for-date}})))

(def LocalDateThisYear
  [Date {:format "YYYY-MM-DD", :min "2020-01-01", :type java.time.LocalDate}])

(m/validate LocalDateThisYear #time/local-date "2020-12-12") ; => true

(m/decode LocalDateThisYear "2020-12-12" mt/string-decoder) ; => #time/local-date "2020-12-12"

ikitommi 2020-09-24T16:52:12.014100Z

(def registry
  (merge (m/-predicate-schemas)
         (m/-class-schemas)
         (m/-comparator-schemas)
         (m/-base-schemas)
         (m/-type-schema) ;; <--- new too
         {:zoned-date-time (m/-simple-schema {:type :zoned-date-time, :pred #'zoned-date-time?})
          :local-date      (m/-simple-schema {:type :local-date,      :pred #'local-date?})}))

2020-09-24T16:54:05.014700Z

@ikitommi: Thank you for all this help/context/insight!

ikitommi 2020-09-24T19:01:01.015400Z

added tests and merged the lazy registries & lazy multi. Here’s the final api:

(def registry
  (mr/lazy-registry
    (m/default-schemas)
    (fn [type registry]
      ;; simulates pulling CloudFormation Schemas when needed
      (let [lookup {"AWS::ApiGateway::UsagePlan" [:map {:closed true}
                                                  [:Type [:= "AWS::ApiGateway::UsagePlan"]]
                                                  [:Description {:optional true} string?]
                                                  [:UsagePlanName {:optional true} string?]]
                    "AWS::AppSync::ApiKey" [:map {:closed true}
                                            [:Type [:= "AWS::AppSync::ApiKey"]]
                                            [:ApiId string?]
                                            [:Description {:optional true} string?]]}]
        (println "... loaded" type)
        (some-> type lookup (m/schema {:registry registry}))))))

;; lazy multi, doesn't realize the schemas
(def CloudFormation
  (m/schema
    [:multi {:dispatch :Type, :lazy-refs true}
     "AWS::ApiGateway::UsagePlan"
     "AWS::AppSync::ApiKey"]
    {:registry registry}))

(m/validate
  CloudFormation
  {:Type "AWS::ApiGateway::UsagePlan"
   :Description "laiskanlinna"})
; ... loaded AWS::ApiGateway::UsagePlan
; => true

(m/validate
  CloudFormation
  {:Type "AWS::ApiGateway::UsagePlan"
   :Description "laiskanlinna"})
; => true

❤️ 2
2020-09-25T15:19:17.016500Z

Thank you!

ikitommi 2020-09-24T19:03:18.016Z

has also the :multi key spell-checker:

(deftest multi-error-test
  (let [schema [:multi {:dispatch :type}
                ["plus" [:map [:value int?]]]
                ["minus" [:map [:value int?]]]]]

    (is (= {:type ["invalid dispatch value"]}
           (-> schema
               (m/explain {:type "minuz"})
               (me/humanize))))

    (is (= {:type ["did you mean minus"]}
           (-> schema
               (m/explain {:type "minuz"})
               (me/with-spell-checking)
               (me/humanize))))))