Fork me on GitHub
#malli
<
2021-07-16
>
hansbugge09:07:37

Hi, I'm experiencing something funny with humanize in Malli 0.5.1. When the wrapper function returns a string, then the :malli/error key gets an entry for each error, but when it returns anything but a string then there will be only one error under :malli/error.

(-> (m/explain [:and
                [:fn (constantly false)]
                [:fn (constantly false)]]
               {:a :map})
    (me/humanize {:wrap (constantly "a string")}))
;; => #:malli{:error ["a string" "a string"]}

(-> (m/explain [:and
                [:fn (constantly false)]
                [:fn (constantly false)]]
               {:a :map})
    (me/humanize {:wrap (constantly :not-a-string)}))
;; => #:malli{:error [:not-a-string]}

hansbugge09:07:06

And I'm having a hard time understanding why from looking at the source. Am I right that this is a bug?

hansbugge09:07:30

It seems to be the form (if (-just-error? v) (into (vec e) v) v) in malli.error/-put, where -just-error? only returns true if the value is exactly a vector with one string

hansbugge10:07:29

My problem is that we're using explainers and humanize to generate structured form validation output, where the wrapper passed to humanize generates a map with the error message and a couple of other fields. But this means we get at most one error in :malli/error.

ikitommi13:07:07

just wrote a issue based on old PR to make the :and error handling more robust, e.g. take the first error form and accumulate to that. Not sure if that would help here, but coming anyway: https://github.com/metosin/malli/issues/476

ikitommi15:07:22

nice to see how things click while developing malli. • in spec1, s/and flows conformed values, which is IMO not intutive, but needed for special cases (e.g. parsing sequential input), source of much confusion • in spec1, s/cat forced things to be named, need you it or not • in spec2, there might be a non-conforming variant and', most likely no cat' thou. with malli, the simple syntax is the default and you can detail it (named branches, parsed childs) if needed. Spec has amazing ideas and personally is a big source of inspiration. The Malli evolution: Given:

(defn distance [min max] 
  (- max min)
Simplest description:
;; int int -> int
[:=> [:cat :int :int] :int]
Adding names:
;; min:int max:int -> int
[:=> [:catn [:min :int] [:max :int]] :int]
Adding constraints on input (with new parsed utility schema):
;; min:int max:int [min < max] -> int
[:=>
 [:and
  [:catn
   [:min :int]
   [:max :int]]
  ;; here we want to used parsed values instead of orginal sequence
  [:parsed
   [:fn
    {:error/message "min should be lass than max"}
    (fn [{:keys [min max]}] (< min max))]]]
 :int]

👏 7
greg17:07:27

Haha, I've spend a couple of hours trying to understand the logic behind it recursive traversal of transformers until I discovered it is at the Schema implementation, in the transformer- fn 😄

👌 3