This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-08-17
Channels
- # announcements (8)
- # atom-editor (8)
- # aws (1)
- # babashka (96)
- # beginners (128)
- # calva (7)
- # cider (12)
- # cljsrn (1)
- # clojure (75)
- # clojure-europe (28)
- # clojure-hamburg (2)
- # clojure-italy (7)
- # clojure-nl (7)
- # clojure-norway (3)
- # clojure-uk (13)
- # clojurescript (26)
- # conjure (2)
- # cursive (18)
- # data-science (7)
- # datalog (21)
- # datomic (9)
- # duct (15)
- # expound (29)
- # figwheel-main (14)
- # fulcro (59)
- # helix (4)
- # jobs (2)
- # kaocha (19)
- # leiningen (15)
- # luminus (4)
- # malli (57)
- # meander (2)
- # off-topic (2)
- # pathom (12)
- # pedestal (8)
- # re-frame (53)
- # reitit (9)
- # remote-jobs (1)
- # shadow-cljs (64)
- # spacemacs (1)
- # specter (2)
- # tools-deps (12)
- # tree-sitter (2)
- # vim (11)
- # xtdb (17)
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.
Just realized it's not what I want, the schema walker walks the schema, not the actual data. I still need to walk the actual data to get the pat to generate the unique key for each map in the tree based on its actual location. Also, my schema is recursive - think about a file system, where there are directories as maps, files as leaves, and sub-directories as child maps.
walk + decode would work for non-recursive schemas. Please write an issue, I'll think about the solution.
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
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
> 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.
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 ...})}}
Just realized it's not what I want, the schema walker walks the schema, not the actual data. I still need to walk the actual data to get the pat to generate the unique key for each map in the tree based on its actual location. Also, my schema is recursive - think about a file system, where there are directories as maps, files as leaves, and sub-directories as child maps.
@steveb8n I've been playing with your demo (https://github.com/stevebuik/fork-malli-ideas) , it's a really nice approach to form validation.
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
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.
@ramblurr https://github.com/metosin/malli/pull/243/commits/7d335e916d9769d4039b69e1475f04f3239ac5c7
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)}))
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,
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
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
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