is it possible for my transformer/decode to know the current path when decoding? e.g. in the below example, I'd like to replace the <location>
with :k1
(ma/decode
[:map
[:k1 [:map {:decode/foo #(assoc % :location :<location?>)}
[:x int?]]]]
{:k1 {:x 1}}
(mt/transformer
{:name :foo}))
;; => {:k1 {:x 1, :location :<location?>}}
the background story is I want to generate a unique key for each such map based on its path in the whole tree
@linshuai2012 not at the moment, but you can do the following:
1) use m/walk
to transform the schemas by adding a :in
property to schemas (Schema :path
and value :in
are available for walkers)
2) create a decoder that uses the interceptor :compile
hook to access the Schema at decoder creation time
there are few pending PRs around walkers, not 100% sure what is in the current master.
@ikitommi thx! I'll take a look at this approach. Currently I'm trying a solution that 1. leaves a place holder when decoding, and then 2. after decoding the whole tree, walk my tree on my own (and accumulate the path), and for each such map, use clojure.walk/prewalk to replace the placeholder with the current path
pretty sure it's not very performant that way. There is an example of attaching a generated sample value to all schemas in the readme.
It uses the m/schema-walker
, you need the plain walker to access the :in
data. But otherwise, the 1) should be copyable from that.
also, the interceptor :compile
hook could publish more information about the context to the transformers, last call to break the api before freezing things. Please write an issue if you want that
It is now a callback fn with args of schema value
, could be schema value path in root-schema
for example
the m/walker apporach looks promising, I'll try it next
I'm just getting started exploring malli
https://github.com/metosin/malli#mallicorebase-schemas seem to indicate if I want a :int
or :int-in
schema, I'd have to register it myself, is that correct?
@ramblurr you can just create a function that returns a form for it:
(defn int-in [min max]
[:and int? [:fn '(fn [x] (< min x max))]])
but you should add a :gen/gen
for it too. I think there should be a built-in for that (and for dates too)
[:int {:min 1, :max 10}]
[:date {:min "2020-08-16", :max "2020-10-10"}]
kinda thingsthe raw impl would look much like :string
: https://github.com/metosin/malli/blob/master/src/malli/core.cljc#L772-L805
ah great, i see
is there an aggregated list of built ins somewhere (with docs as to their properties)
> I think there should be a built-in for that (and for dates too) Do you mean you think this built-in already exists, or you had the idea that it should be added ? ๐
(m/default-schemas)
gives a list of all. the source code is currently the best source of descriptions.
should be added ๐
the properties - will add description of those using malli (eat your own..), but not there yet.
Using the int-in
function def you gave an example for above.. if you added it to a registry {:int-in (m/fn-schema :int-in int-in)}
, how could it be consumed later? In fact, this wouldn't work right?
You could do something like {:int-in-0-10 (m/fn-schema :int-in-0-10 (partial int-in 0 10))}
I suppose
(defn int-in [min max]
[:and int? [:fn `(fn [x] (< ~min x ~max))]])
(def int-in-1-10 (int-in 1 10))
(validate [:tuple int-in-1-10 (int-in 10 100)] [2 12])
; => true
(form [:tuple int-in-1-10 (int-in 10 100)])
;[:tuple
; [:and int? [:fn (clojure.core/fn [malli.core/x] (clojure.core/< 1 malli.core/x 10))]]
; [:and int? [:fn (clojure.core/fn [malli.core/x] (clojure.core/< 10 malli.core/x 100))]]]
if you write an issue about the :int
as built-in, happy to add that. much cleaner
Am I correct that you can't use the generic :int-in
as defined above in a registry, because the "registered" specs must be predicate predicate functions (single value as input)?
โข registered schemas are one of: 1) IntoSchema
instance (e.g. something that take the schema syntax and return a Schema
. 2) Schema
instance, 3) Schema syntax.
โข m/fn-schema
just takes a predicate fn, which is only used to build a validator for the schema
โข there could be more helpers to build custom schemas easier, something between m/fn-schema
(here: too simple) and writing IntoSchema
impl (here: too much work) by hand, but currently, there is not.
I think Iโll extract the code from :string
so that :date
, :number
, :date-time
etc can reuse most of it (e.g. the :min
+ :max
handling of via properties, effecting both validation and value generation)
also, you could build your own things with it easily: {:registry {:bigdec (m/-ranged-pred-schema {:pred bigdec?, :range-pred โฆ, :range-gen ...})}}
walk + decode would work for non-recursive schemas. Please write an issue, I'll think about the solution.
@steveb8n I've been playing with your demo (https://github.com/stevebuik/fork-malli-ideas) , it's a really nice approach to form validation.
Yeah pretty cool!! ๐
in 2.1.4 you can add a keywordize-keys true option to work exclusivley with keywords/namespaced keywords instead of strings, also in the validation
The keyword <-> string transformations are clever too, though don't work when namespaced keys are used
@ramblurr https://github.com/metosin/malli/pull/243/commits/7d335e916d9769d4039b69e1475f04f3239ac5c7
merged in master
first look comment: the max range should be exclusive, not inclusive. That's a pretty standard trope across all range checks in almost any language I know
with m/-simple-schema
it should be easy to add new schemas that use properties in validation:
(-simple-schema {:type :double, :pred double?, :property-pred (-min-max-pred identity)}))
:thinking_face:
spec has that, test.check uses inclusive for both.
could you link some external wisdom for that?
https://clojuredocs.org/clojure.spec.alpha/int-in-range_q is exclusive
https://clojure.github.io/test.check/clojure.test.check.generators.html#var-large-integer* is inclusive
JSON Schema has inclusive: https://json-schema.org/understanding-json-schema/reference/numeric.html
and so s/int-in and s/double-in are exclusive, https://clojuredocs.org/clojure.core/range is exclusive,
json schema supports inclusive and exclusive options ๐
to be clear, i'm advocating only the max
be exclusive, look at pretty much and langugage: python's list slice operator, java's IntStreams,
here's an argument why: http://wrschneider.github.io/2014/01/07/time-intervals-and-other-ranges-should.html (starting off about time, but then at the end mentions integer ranges)
Haha, and https://stackoverflow.com/questions/8441749/representing-intervals-or-ranges is a link to http://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html by Dijkstra on the topic
thanks. will read those.
That said, I don't need to die on this hill ๐ Just sharing my experience that generally when I see a range in an api, I assume (and assumed others did too!) that it was inclusive start and exclusive end. As long as it's documented, it'll be ok either way
Thx! I will.
I'm working around this using
(defn unkeyword [m]
(map-keys #(subs (str %) 1) m))
at the end of validator-for-humans
to turn :foo/attr
-> "foo/attr"
.. and of course using "foo/attr" as the name in the form form. Works well enough, if a little dirty.
meanwhile:
(mg/generate
[:map
[:string :string]
[:int :int]
[:double :double]
[:boolean :boolean]
[:keyword :keyword]
[:symbol :symbol]
[:qualified-keyword :qualified-keyword]
[:qualified-symbol :qualified-symbol]]
{:size 42, :seed 42})
;{:string "ยฆยฎGรVรก@ยฃยฐ5o,&ยต7\rรรฃ",
; :int -1251,
; :double -0.03125,
; :boolean true,
; :keyword :WD_VS_-r,
; :symbol k5K_2i,
; :qualified-keyword :M8qL/u?RAmf,
; :qualified-symbol x/y0T}
related: https://github.com/metosin/malli/issues/25@lucio nice to see some interest