This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-03-15
Channels
- # aatree (1)
- # atlanta-clojurians (3)
- # beginners (112)
- # boot (4)
- # boot-dev (1)
- # bristol-clojurians (1)
- # cider (55)
- # cljs-dev (23)
- # cljsjs (1)
- # cljsrn (7)
- # clojars (24)
- # clojure (84)
- # clojure-brasil (1)
- # clojure-china (1)
- # clojure-italy (27)
- # clojure-norway (17)
- # clojure-romania (1)
- # clojure-spec (109)
- # clojure-uk (92)
- # clojurescript (94)
- # community-development (1)
- # core-matrix (1)
- # cursive (12)
- # datascript (1)
- # datomic (23)
- # figwheel (1)
- # fulcro (17)
- # hoplon (11)
- # jobs-discuss (3)
- # keechma (6)
- # lein-figwheel (4)
- # leiningen (79)
- # lumo (32)
- # mount (42)
- # off-topic (22)
- # onyx (13)
- # parinfer (30)
- # portkey (47)
- # powderkeg (1)
- # programming-beginners (24)
- # protorepl (3)
- # re-frame (16)
- # reagent (100)
- # ring-swagger (7)
- # shadow-cljs (134)
- # spacemacs (3)
- # sql (1)
- # tools-deps (48)
- # uncomplicate (1)
- # unrepl (14)
- # yada (1)
I don't think you could instrument it (which I assume is the point) at runtime, since it's normally inlined
I'm curious how easily compilation could be modified to avoid the inlining does cljs spec do runtime instrumentation at all? I don't even know that
How does one go about adding functionality to spec? I tried to extend s/keys so that it also did a contains check, basically making it a closed-key spec. I did so by wrapping s/keys in a separate macro which takes the same input then s/keys and returns an anded spec of keys and one which will validate that no extra keys are present. But composing macros like that isn't great. And so now I'm trying to extend it even more, so that my closed-keys macro can be used in a s/merge style. Anyways, bottom line is, the experiment has been tedious. So I was thinking, maybe instead of building on top of s/keys, I can just build a whole new spec, but how do you do that?
I also found it frustrated all the specs were macros, which some of them were plain old functions that took clojure data. Like s/keys could have taken a map, or a vector of vectors, instead of a custom macro DSL.
@didibus current version of spec doesn’t really support extensions. But you can reify clojure.spec.alpha/Spec
and copy-paste current spec internals to make something similar.
I think s/keys
could be rewritten as a function, as all keys need to be referenced via keyword. Something like: https://github.com/metosin/spec-tools/blob/master/src/spec_tools/data_spec.cljc#L39-L68.
.. actually the closed keys would be easy to implement on top of that via extra :closed?
key or similar. hmm.
If you were writing an fdef for a simple function that takes a collection of strings (at least one) could you use (s/+ string?)
instead of (s/coll-of string?)
for the :args
.
(s/fdef my-fn
:args (s/cat :item (s/+ string?))
or
:args (s/cat :item (s/coll-of string?))
..)
Those are different though, (s/cat :item (s/spec (s/+ string?)))
works the same as the latter.
@U0J9LVB6G ah thanks!
@U0J9LVB6G what was the difference out of interest? What does s/spec do to it?
(s/conform (s/cat :item (s/+ string?)) ["a" "b"])
(s/conform (s/cat :item (s/spec (s/+ string?))) [["a" "b"]])
(s/conform (s/cat :item (s/coll-of string?)) [["a" "b"]])
absolutely
I would even say that’s preferred (although you won’t really see much practical difference)
generally when spec’ing function signatures, you’re spec’ing syntax, and spec regex ops are the best match so I usually try to stick to those in fdef args
when spec’ing function args, favor regex op specs
when spec’ing other kinds of data, I favor collection specs
I have a spec:
(s/keys :req-un [::title
::content]
:opt-un [::icon
::widget-class
::settings
::collapsed?
::dropdowns
::controls
::preview?
::tabs
::collapse-opts
::help])
Now I want to have a warning for myself when someone passes something that has more keys than I expected. Can I use my spec for this or do I have to duplicate the keys?@borkdude You can extract the list of keys from the spec form itself, and write a predicate that checks those are the only keys. Stu Halloway posted an example on the mailing list a while back I think... Probably in a Gist somewhere...
https://github.com/gfredericks/schpec/blob/b2d80cff29861925e7e3407ef3e5de25a90fa7cc/src/com/gfredericks/schpec.clj#L13-L35 here’s a macro to make closed keys specs and here’s Stu’s gist which I think was more meant for finding “forgotten” key specs https://gist.github.com/stuarthalloway/f4c4297d344651c99827769e1c3d34e9
@taylor Ah, yes. I misremembered. Stu's code was to find any keys in a spec that did not have definitions.
But this code from Stu's Gist is similar to what you'd need (if you don't want to go with shpec
): https://gist.github.com/stuarthalloway/f4c4297d344651c99827769e1c3d34e9#file-missing_keys_specs-clj-L31-L33
I want to use schpec in clojurescript, but now I also have to bring in test.check.. ok
Hmm, I already have that… maybe I should upgrade. I get errors about clojure/spec/gen/alpha
What sort of errors?
• public/js/app.js
WARNING: Use of undeclared Var com.gfredericks.schpec/limit-keys at line 53 src-cljs/dre/components/widget.cljs <-- this is my namespace
WARNING: No such namespace: clojure.spec.gen.alpha, could not locate clojure/spec/gen/alpha.cljs, clojure/spec/gen/alpha.cljc, or JavaScript source providing "clojure.spec.gen.alpha" at line 53 src-cljs/dre/components/widget.cljs
WARNING: Use of undeclared Var clojure.spec.gen.alpha/fmap at line 53 src-cljs/dre/components/widget.cljs
my ns:
(ns dre.components.widget
(:require
[clojure.spec.alpha :as s]
#_[com.gfredericks.schpec :refer-macros [excl-keys]]
[dre.page-util :refer [open value-of in-iframe?]]
[dre.react-util :refer [Collapse DropdownButton MenuItem]]
[reagent.core :as r]
[taoensso.timbre :refer-macros [info warn debug]])
(:require-macros
[com.gfredericks.schpec :refer [excl-keys]]))
I first tried the one with #_
but then I get:
No such namespace: com.gfredericks.schpec, could not locate com/gfredericks/schpec.cljs, com/gfredericks/schpec.cljc, or JavaScript source providing “com.gfredericks.schpec” in file src-cljs/dre/components/widget.cljs
Does clojure.spec.gen.alpha exist in cljs?
Interesting.
cljs should rewrite the clojure to cljs one automatically, but you would need it in a cljc file
@gfredericks you could also put everything in a .cljc file with the help of macrovich… just wrap every macro in macros/deftime
@borkdude I have not tried it in cljs; schpec hasn't gotten a lot of attention. I had the silly idea that I could just make a generic bucket to put things in and then people would put things in it and use it, but I think people prefer to make libraries with more specific purposes
but I'm happy to incorporate any improvements you have, like making sure it works in cljs
hi, I'm wanting to make a model in spec of my data where the values for 2 keys in my map are dependant on another key's value. I've watched Stu's blog post about using gen/bind
and gen/fmap
; are there any other examples someone could point me to?
@alexmiller we’re very confused about this behavior, wondering if it has come up before:
user> (s/conform #{11 false :foo} false)
:clojure.spec.alpha/invalid
the behavior seems to be in this condition: https://github.com/clojure/spec.alpha/blob/master/src/main/clojure/clojure/spec/alpha.clj#L875-L877
you should never use logically false values in literal sets
(and this applies to more than spec - some
is another case)
yeah I guess that really messes things up
use false?
as a predicate
thanks
or s/nilable
this particular case does not typically come up in general use very often in my experience
what you said actually makes total sense, it just got me puzzled here for a minute
I guess we’ll always need to return logical true
for valid?
to work
@gfredericks This is how far I got, but I still get errors: https://github.com/borkdude/schpec/blob/master/src/com/gfredericks/schpec.cljc
The errors being:
WARNING: No such namespace: clojure.spec.gen.alpha, could not locate clojure/spec/gen/alpha.cljs, clojure/spec/gen/alpha.cljc, or JavaScript source providing "clojure.spec.gen.alpha" at line 54 src-cljs/dre/components/widget.cljs
WARNING: Use of undeclared Var clojure.spec.gen.alpha/fmap at line 54 src-cljs/dre/components/widget.cljs
when I call the macro excl-keys
I’ll try to change the require into:
(:require [clojure.spec.alpha :as s]
[clojure.spec.gen.alpha :as sg]
[clojure.set :as set]
#?(:clj [net.cgrand.macrovich :as macros]))
which I think should “just work” ™️ in cljs as wellMy apologies. cljc and macros can be a pretty gnarly combination
I also ran into this issue with cljc, macros and clojurescript: https://dev.clojure.org/jira/browse/CLJS-2636
I have a spec like this: (s/def (s/map-of ::id ::person)
where ::id
must be a property of ::person
. Could anyone help me making the spec with s/with-gen
so I can generate this map for testing?
(s/def :entity.person/id string?)
(s/def :entity/person (s/keys :req [:entity.person.id])
Indexed collections are structures I've come to make a lot of times doing clojure.
Ie, I rather do (get-in state [:people id :name])
than have to find the vector index every time.
@seancorfield @borkdude keep in mind that any attempt to analyze specs won’t work for multi-specs in CLJS, at least AFAICT
(which limits our ability to do things like look for undefined specs, or to automatically add checkers for missing values)
@pablore I don’t have time to give you a full answer, but I would start with a generator that creates a collection of ::person (which will have ::id’s in it). then gen/fmap over that generator to construct the map from the ids in the generated person entities
something like (gen/fmap (fn [ps] (zipmap (map ::id ps) ps)) #(s/gen (s/coll-of ::person)))
This almost worked! Just has to replace ::id with :id, because (s/keys) generates with keys with single colons
(defn gen-indexed
[spec idx]
(gen/fmap (fn [xs] (zipmap (map idx xs) xs))
(s/gen spec)))
Obviously my data is more complicated than that — it's an number of vectors enclosed one in other. I'm trying to use spec for validation, so it's an external data type, not something I can change.
seriously though, do you know how many levels of vectors?
Not a "nice" uniform CSV, a complicated one. It's got a multiline header and a multiline footer, and empty strings delimiting data sections.
well, spec the inner levels, then spec the outer levels out of those :)
(s/def ::line (s/coll-of string? :kind vector?))
That's what I tried. s/cat
on outer level, then s/cat
on inner level, and first predicate in inner level tried to match the whole line, not the first element of an array.
(s/def ::header-fields (partial = ["Account Number"]))
(s/def ::account-number (s/and string?
(partial re-matches #"^[0-9]{16}$")))
(s/def ::header-info (s/cat ::account-number ::account-number))
(s/def ::delimiter (partial = [""]))
(s/def ::header (s/cat ::header-fields ::header-fields
::header-info ::header-info))
(s/def ::statement (s/cat ::header ::header
::delimiter ::delimiter))
This ought to match [["Account Number"] ["1234123412341234"] [""]]
, but apparently the specs for sequences are flattened.
Because
(s/explain ::statement data)
In: [1] val: ["1234123412341234"] fails spec: :bacc.foo/account-number at: [:bacc.foo/header :bacc.foo/header-info :bacc.foo/account-number] predicate: string?
Ah, if I add (s/and (s/coll-of string? :kind vector) ...)
to the ::header-info
then in succeeds.
@alexmiller Thanks