haha just my luck, a week after i get started, the docs improve ๐ it looks great!
btw @ikitommi i ended up chunking the results of sets of 100 item m/provide
into an atom and then m/union
ing them together afterward. i ended up with a fair bit of [:or [:or [:or ....]]]
but that was easy to remedy with a postwalk ๐
@robert-stuttaford good to hear. the right solution is to add few new Protocols (e.g. Inferrer
and Provider
) and attach the latter to the reified IntoSchema
s: each instance know how to construct itself given a value and options. Internally using type/class-based lookup tables, it should be 2-4 orders of magnitude faster, and pluggable. Have an pre-initial draft somewhere ๐
that sounds sane!
(malli/encode
[:or [:map
[:password string?]
[:password2 string?]]
[:fn {:error/message "passwords don't match"}
'(fn [{:keys [password password2]}]
(= password password2))]]
{:password "ok"}
(mt/key-transformer {:encode name}))
=> {:password "ok"}
(malli/encode
[:and [:map
[:password string?]
[:password2 string?]]
[:fn {:error/message "passwords don't match"}
'(fn [{:keys [password password2]}]
(= password password2))]]
{:password "ok"}
(mt/key-transformer {:encode name}))
=> {"password" "ok"}
Why doesn't encode work on the first one but does on the second, related to using :or instead of :and I guess.I believe neither of the examples doesnโt do anything, they are just just no-op.
oh, wait.
encode
does not validate and those inputs are invalid, so nasal demons
actually:
(-transformer [this transformer method options]
(let [this-transformer (-value-transformer transformer this method options)]
(if (seq children)
(let [transformers (mapv #(or (-transformer % transformer method options) identity) children)
validators (mapv -validator children)]
(-intercepting this-transformer
(if (= :decode method)
(fn [x]
(reduce-kv
(fn [x i transformer]
(let [x* (transformer x)]
(if ((nth validators i) x*) (reduced x*) x)))
x transformers))
(fn [x]
(reduce-kv
(fn [x i validator] (if (validator x) (reduced ((nth transformers i) x)) x))
x validators)))))
(-intercepting this-transformer))))
Although in terms of the implementation I think both of those should be no-ops
it does validate and the first one fails -> doesnโt transform
Ah yes so :or
and seqex schemas validate when encoding because they have to pick a branch but nothing else (e.g. :and
does)
But that is implementation detail
The recommended flow is first validate
, then encode
if ok, else explain
Ceterum censeo, maybe some day we will have a thing that does all of that in one pass like Plumatic coerce
Thanks for the explanations !
This is my pet peeve and now I finally added an issue https://github.com/metosin/malli/issues/404 But it is a big decision and feature
is it possible for malli to report all errors instead of stopping on the first one?
explain(er)
should report all the errors. What are you seeing / not seeing?
ah, apologies. i'm using explain -> with-spell-checking -> humanize
seems humanize is dropping it
really? should not.
lemme make reaaaal sure first, don't want to waste your time ๐
ok, it's not humanize. in the explain, the last error that i get is a :malli.core/input-remaining
. when i fix that issue, then i get a new previously unreported error further down (happens to be the same issue: a kv-pair that should be one map level up)
so, is there some way have the whole sequence reported on at once?
basically, i'd like to use malli specs to provide a structural EDN linter in a web-based cms code editor. but for this one thing, it's working, beautifully
i am getting other errors from other collections, but the same issue manifests, in that i only get one error per such collection. i guess the question i'm asking is, how do i make :sequential and/or :+ check all elements
Oh, the partial seqexp, no reporting / transformation on partially matched sequences atm. https://github.com/metosin/malli/issues/387
ok so this is because i'm using :+ rather than :sequential?
not sure, could you give an example with actual and expected result?
ok, i switched to sequential, and this is the reason - thank you!
You should use e.g. :sequential
instead of :*
/`:+` if you can
i can live without ensuring the sequence has at least one item
[:sequential {:min 1} :int]
ok rad i've got what i need. malli is lovely
now to use edamame's edn metadata stuff to wire up go-to-line links
In general the seqex engine can't "resynchronize" after an erroneous element.
We could have some optimizer that recognizes [:+ :int]
and transforms it to [:sequential {:min 1} :int]
but it would add complexity and unpredictability...
totally fair
did a malli + edamame poc some time ago, canโt recall was it any good, but pushed just as a gist: https://gist.github.com/ikitommi/0e5c4e48d8aeb7dd176128856ecdacb5
something @borkdude was asking I think.
what I tried to do there is "complect" error message with locations directly
but I think you can just do explain and then look up the vals in the paths which have locations (or maybe they are already in the errors) and then postwalk the result to add the locations to the error messages
that second thing is what i'm doing
yeah @ikitommi your gist is the beautiful general version of the ugly hack i'm busy writing. going to start again with yours, thank you
That code looks familiar :thinking_face:
@robert-stuttaford I think the second thing is probably the easiest?
looking at the code, it has transduce
, I never use that. I canโt recall the origins of the code, just from one of the zillion scratch files. could be @nilern s work too :thinking_face:
Looking at the Metosin Slack history I did the unwrap
and collect
bits and remarked that the prewalk
makes it O(n^2) but it only matters if there are large map keys or set members which hardly ever happens...
Anyway it took a while to write so it would be cool if it found some use