This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-04-20
Channels
- # announcements (3)
- # babashka (7)
- # beginners (36)
- # calva (71)
- # cider (25)
- # clj-commons (5)
- # cljdoc (19)
- # cljs-dev (5)
- # clojure (223)
- # clojure-austin (2)
- # clojure-bay-area (1)
- # clojure-europe (31)
- # clojure-france (6)
- # clojure-nl (2)
- # clojure-norway (19)
- # clojure-spec (13)
- # clojure-uk (7)
- # clojurescript (127)
- # core-logic (2)
- # cursive (21)
- # datalevin (53)
- # datomic (9)
- # emacs (37)
- # events (1)
- # graphql (8)
- # jobs (12)
- # lsp (8)
- # off-topic (92)
- # pathom (49)
- # pedestal (1)
- # polylith (3)
- # re-frame (25)
- # releases (2)
- # sci (11)
- # shadow-cljs (13)
- # vim (10)
i wanted to generate some datomic lookup-refs with spec, but s/cat
always returns a seq
, not a vector
.
is there some built-in / idiomatic / recommended way to spec vectors?
i saw there are :kind
and :into
options for s/coll-of
, but i couldn't combine it with s/cat
; i always got Couldn't satisfy such-that predicate after 100 tries.
error.
i found a recommendation here:
https://gist.github.com/mhuebert/8fdeedae57bf797778054dcf8f33ab8b
but my gut feeling tells me, that there might be a simpler way.
unfortunately, there is not a simpler way - it is very challenging (in spec 1) to spec vectors and get all of conform, unform, gen etc to work. we've fixed this in spec 2 but I don't have a good answer in spec 1.
for some limited cases, yes (it's fixed size and can only be positionally described, not with the regex ops)
I just started looking into spec2.
Found this page, which specifically mentions how to solve my issue with non-flowing s/and-
:
https://github.com/clojure/spec-alpha2/wiki/Differences-from-spec.alpha
(s/def ::vcat (s/and- (s/cat :i int?) vector?))
Thanks @U064X3EF3 for the great docs!
not mentioned there, but that's actually reified for you in s/catv (vector-only) and s/cats (sequence-only) in spec 2
I don't think that ever made it back to those docs, but they're there
and before you ask, no I would not recommend using spec 2 at this time, it has a number of bugs :)
Is there a way to pass along/access/use the overrides from a top-level call to s/gen
inside a custom generator defined with s/with-gen
or (s/spec ... :gen ...)
?
Example.
Supposed I want to generate some sort of query with the keys :database
and :table
. I'd like to have a custom generator for :table
so its values are based on the value of :database
... however if I use an overriden generator for :database
That generator is invisible from inside my custom generator.
(s/def ::database string?)
(s/def ::table
(s/with-gen
string?
(fn []
(gen/bind
(s/gen ::database)
(fn [db]
(gen/fmap (fn [table]
(str db \. table))
gen/string))))))
(s/def ::query
(s/keys :req-un [::database ::table]))
(gen/sample (s/gen ::query {::database (fn [] (gen/return "my_database"))}))
;; =>
[{:database "my_database", :table "."}
{:database "my_database", :table "7.$"}
{:database "my_database", :table "T2."}
{:database "my_database", :table "xv.H¨"}
{:database "my_database", :table "aB.Ín"}
{:database "my_database", :table "Q3MCh.Çá"}
{:database "my_database", :table "F68.·%:"}
{:database "my_database", :table ".>ì"}
{:database "my_database", :table "jBk.ÇP µrfL"}
{:database "my_database", :table "d.¥¶«â\b"}]
Is there some way to do this? I know I could do the whole thing with test.check.generators/let
or bind
but I'd be missing out on all the niceness from spec maps since AFAIK there's nothing like s/keys
in test.check
I guess I could get it working by redefining the entire spec with s/def
instead of trying to pass an override for the generator, or maybe just with-redefs
-ing clojure.spec.alpha/registry-ref
itself... both feel icky but since this just for generative testing it's probably not the end of the world.
Another option might be to replace all my specs with functions that return specs based on previous state, but for map specs I guess I'd have to use Spec Alpha 2 so I could programmatically create them with schema
or the like. Maybe that's a bit better. But creating a bunch of ad-hoc specs programatically actually seems like more work than if I forgot about spec altogether and just used test.check.generators
directly
I don't think there's any way provided in spec to make your custom generator see the override (you could get creative and bind a dynamic var, but that's outside of specs functionality and probably a bit unexpected). test.check does have hash-map, which will generate a map of keys to values like (gen/hash-map :database db-gen :table table-gen)
. I would probably use fmap though here and do something like:
(gen/fmap (fn [[db-str table-str]] {:table (str db-str \. table-str) : database db-str})
(gen/tuple (gen/return "my_database") gen/string))
Most things can be solved using fmap and then reconciling the generated parts in the fmap fn somehow. This gives better shrinking as well than using bind/let.I tried binding dynamic vars at every possible place but couldn't figure out how to get it to work consistently.
Thanks, I think I'll just stick to using test.check
stuff without spec for now. I managed to put together a generator that works sort of like s/keys
with :opt
or :opt-un
, not sure how well it's going to handle the shrinking. I'll have to wrap my head around that a bit more