Hi, I'm just working through the Readme tutorial on the latest 0.3.0 and found that this example failed:
(m/validate
[:map
["status" [:enum "ok"]]
[1 any?]
[nil any?]
[::a string?]]
{"status" "ok"
1 'number
nil :yay
::a "properly awesome"})
; => true
Instead I get an exception:
1. Unhandled clojure.lang.ExceptionInfo
:malli.core/naked-keys-not-supported nil
{:type :malli.core/naked-keys-not-supported, :data nil}
Is this a regression or API change? I couldn't find references to "naked keys" apart from internal impl code
@qythium it was a regression, fixed in master
Is there a way to filter data by a schema? So if I have data of the form {:a 1 :b 2} and a schema of the form [:map [:a int?]] can I "apply" the schema to the data in order to get {:a 1}? All I can think of right now is to use m/explain then parse the errors.
@raymcdermott would this be ok:
(def Org-Ref
[:map {:title "Organisation name"}
[:ref {:swagger/description "Reference to the organisation"
:swagger/example "Acme floor polish, Houston TX"} :string]
[:kikka [:string {:swagger {:title "kukka"}}]]])
(defn remove-swagger-keys [p]
(not-empty (apply dissoc p (into #{:swagger} (->> p (keys) (filter (comp #{:swagger} keyword namespace)))))))
(defn walk-properties [schema f]
(m/walk
schema
(fn [s _ c _]
(m/into-schema
(m/-parent s)
(f (m/-properties s))
(cond->> c (m/entries s) (map (fn [[k p s]] [k (f p) (first (m/children s))])))
(m/options s)))
{::m/walk-entry-vals true}))
(walk-properties Org-Ref remove-swagger-keys)
;[:map {:title "Organisation name"}
; [:ref :string]
; [:kikka :string]]
e.g. walk the entrys, un-walk on the way back. apply f
on all properties (entrys & schemas)
@roseneck you can transform the value using strip-extra-keys-transformer
:
(m/decode [:map [:a int?]] {:a 1, :b 2} (mt/strip-extra-keys-transformer))
; => {:a 1}
@ikitommi perfect, thank you very much!
yes @ikitommi that's very elegant
is it possible to introspect the malli schema from inside an :error/fn
fn ? i.e. i want to (first (m/children schema))
so i can print out the enum values in the error message
@robert-stuttaford sure, the fn takes the explain error map as argument, it has the :schema
key.
there are lot of examples in malli.error
https://github.com/metosin/malli/blob/master/src/malli/error.cljc#L75-L80
how do I transform the keys of a schema? e.g., I have [:map [:a-k string?] [:b-k string?]]
and I want to derive a schema that is the same but with snake-case keys: [:map [:a_k string?] [:b_k string?]]
when i try this, i get m/children
isn't a thing, because it's SCI that's running this code
(thank you for your quick response)
Execution error (ExceptionInfo) at sci.impl.utils/throw-error-with-location (utils.cljc:51).
Could not resolve symbol: m/children
is there a trick to get it to see malli?
@robert-stuttaford just out of curiosity: what is your use case for malli + SCI?
malli could make this namespace available inside of sci
there is ::m/sci-options
to override the bindings. The default bindings are:
(defn -default-sci-options []
{:preset :termination-safe
:bindings {'m/properties properties
'm/type type
'm/children children
'm/entries entries}})
have the sci-options changed? donโt seem to work anymre:termination-safe
is removed at least
the options have not been changed, but :bindings
are only valid within the user
namespace, always have been. it's better to use explicit :namespaces
honestly i'm using sci because malli is
yes :termination-safe
has been removed for a while already, also documented in release notes
all i'm doing is writing malli specs at the repl with :error/fn and i ran into an error 'sci not available', so i put it on the CP and onward i went
@robert-stuttaford if you donโt need the seriaization thing, just pass a real function.
oh man. the fn is quoted. shit. sorry for the noise, fellas
๐
that's what I thought :) maybe the error message should be : use sci for serialized schemas
or something
all properties which have functions as values use the malli.eval
, which uses sci
as default for quoted code.
e.g.`:gen/fmap '(partial str "kikka_")`
but, what is the right way to bind those m/children
into sci via options?
{:namespaces {'malli.core {'children m/children}}}
doesnโt work either.
(defn -default-sci-options []
{:namespaces {'malli.core {'properties properties
'type type
'children children
'entries entries}}})
doesn't work = which error?
Execution error (ExceptionInfo) at sci.impl.utils/throw-error-with-location (utils.cljc:50).
Could not resolve symbol: malli.core/chidren [at line 1, column 10]
(defn evaluator [options fail!]
(let [eval-string* (dynaload/dynaload 'sci.core/eval-string* {:default nil})
init (dynaload/dynaload 'sci.core/init {:default nil})
fork (dynaload/dynaload 'sci.core/fork {:default nil})]
(fn [] (if (and @eval-string* @init @fork)
(let [ctx (init options)]
(fn eval [s] (eval-string* (fork ctx) (str s))))
fail!))))
"malli.core/chidren" <- typo?
:face_palm:
thanks, works like a charm
what about binding m
-> malli.core
for not breaking things?
@euccastro try m/walk
with m/schema-walker
check that the schema is a :map
and recreate the children.
thank you!
you can manually insert a (require '[malli.core :as m])
to ensure this works
or (alias 'm 'malli.core)
like:
(defn evaluator [options fail!]
(let [eval-string* (dynaload/dynaload 'sci.core/eval-string* {:default nil})
init (dynaload/dynaload 'sci.core/init {:default nil})
fork (dynaload/dynaload 'sci.core/fork {:default nil})]
(fn [] (if (and @eval-string* @init @fork)
(let [ctx (init options)]
(eval-string* ctx "(alias 'm 'malli.core)")
(fn eval [s] (eval-string* (fork ctx) (str s))))
fail!))))
seems to work
((m/eval 'm/type) :int)
; => :int
๐
is the best way to specify a literal value to use a single-item enum?
nice man
[:= value]
noice!
Tried to use this to build a core.match like this ;) https://gist.github.com/borkdude/26906ee15585ed5e1b7a8eda4cc1ee18
dumb question, how do you get a schema from the registry by its key
I was thinking (malli/-schema malli/default-registry ::sq/sqid)
but -schema
is private
[:map
[:field-a string?]
[:field-b [:one-of
[:map [:status [:= :not-asked]]]
[:map [:status [:= :loading]]]
[:map [:status [:= :failed]]]
[:map [:status [:= :success]
:value [:vector [:map [:id int?]]]]]]]
[:field-c [:one-of
[:map [:status [:= :not-asked]]]
[:map [:status [:= :loading]]]
[:map [:status [:= :failed]]]
[:map [:status [:= :success]
:value [:vector [:map [:id int?]]]]]]]
[:field-d [:set [:map [:id int?]]]]]
what would the idiomatic way to represent this be?
trying on the playground it doesn't say its wrong
but it also doesn't produce any sample values
@emccue there is no one-of
, you can use :or
oh.
yep that did it
could also be a :multi
dispatching on :type
.
@danieleneal try (m/deref (m/schema ::sq/said))
What is the benefit of multi schemas over explicit listing like that?
(m/deref ::sq/said)
might work too.
might not be big difference, but performance. dispatch does one lookup to find the correct schema, :or
does linear scan over all.
I think last question for now - what would be the best way to reuse a structure like this
just a function that takes in the success value schema and returns the whole thing?
@ikitommi, (m/deref ::sq/sqid)
works thanks :thumbsup:
is there a way of getting the schema walker to deref while walking? I've got a schema which is like [:map [:some-key :some-schema] [:another-key :another-schema]]
where :some-schema
and :another-schema
are in the registry. I want to transform all the keys to snake case, but the schema walker doesn't descend into schema references.
@danieleneal see:
> e.g. ::m/walk-refs
& ::m/walk-schema-refs
& ::m/walk-entry-vals
.
walking respects those options, can't recall what does what. Please try, documentation PR welcome
I recall those are recursion safe
e.g. stop of first deref of already walked reference
oh cool
thanks again!!!
@emccue one way to reuse is to use local registry:
[:map {:registry {:user/success [:map
[:status [:= :success]]
[:value [:vector [:map [:id int?]]]]]
:user/default [:map
[:status [:enum :not-asked :loading :failed]]]
:user/field [:multi {:dispatch :status}
[:success :user/success]
[:malli.core/default :user/default]]}}
[:field-a string?]
[:field-b :user/field]
[:field-c :user/field]
[:field-d [:set [:map [:id int?]]]]]
could be of course global registry too.
I mean like, this is analagous to a typed enum from other langs
so i would in my brain go
(remote-data [:map [:id int?]])
and reuse the pattern
oh, sure. itโs just data, so a function liike that is the way to do it.
but by the same token, if i have a spec like this in a namespace
(def user [:map [:name string?]])
I shouldn't use it like this
(def school [:vector other.ns/user])
right?
because then it will get "flattened" and the errors won't be as good
That's how we used to do it with Plumatic Schema
We wanted to enable that style too, you may give up some serialization benefits but get to use normal def
s etc.
The registry names don't play any role in e.g. explainer
actually
so then a local registry is basically equivalent to a let?
(but "runs" in the schema?)