This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-06-21
Channels
- # announcements (9)
- # beginners (222)
- # boot (11)
- # calva (40)
- # cider (1)
- # clj-kondo (10)
- # cljs-dev (1)
- # cljsrn (8)
- # clojars (4)
- # clojure (50)
- # clojure-dev (4)
- # clojure-ecuador (1)
- # clojure-europe (4)
- # clojure-italy (3)
- # clojure-madison (2)
- # clojure-nl (26)
- # clojure-spec (86)
- # clojure-uk (34)
- # clojurescript (11)
- # clr (1)
- # cursive (46)
- # datomic (19)
- # emacs (4)
- # events (1)
- # fulcro (22)
- # graalvm (4)
- # graphql (2)
- # jobs-discuss (40)
- # leiningen (10)
- # luminus (6)
- # nrepl (7)
- # off-topic (18)
- # onyx (6)
- # overtone (1)
- # pedestal (2)
- # planck (1)
- # re-frame (5)
- # reagent (3)
- # reitit (8)
- # rewrite-clj (2)
- # shadow-cljs (139)
- # sql (4)
- # tools-deps (42)
following on from yesterday; I think the tidiest way to achieve
> generator uses id from a database,
> when there is a database
is to use the overrides
feature of (spec/gen)
(doc spec/gen)
-------------------------
clojure.spec.alpha/gen
([spec] [spec overrides])
Given a spec, returns the generator for it, or throws if none can
be constructed. Optionally an overrides map can be provided which
should map spec names or paths (vectors of keywords) to no-arg
generator-creating fns. These will be used instead of the generators at those
names/paths. Note that parent generator (in the spec or overrides
map) will supersede those of any subtrees. A generator for a regex
op must always return a sequential collection (i.e. a generator for
s/? should return either an empty sequence/vector or a
sequence/vector with one item in it)
=> nil
thanks for this btw - am enjoying your writeup! Shame it'll be gone whenever the history runs out
I was thinking that a spec/not
could make sense as exemplified here
(spec/def ::version (fn [s]
...))
(spec/def ::alpha-version (spec/and ::version
(fn []
...)))
(spec/def ::stable-version (spec/and ::version
(spec/not ::alpha-version))) ;; <- imaginary API
has this been considered already?I've written a ton of specs and have never needed an s/not
I've also written a fair number of specs for a few years. Just now I think of not
, so that kind of proof is limited
you could do something like (spec/def ::stable-version #(spec/invalid? (spec/valid? ::alpha-version %)))
in any case, we have no plans to add it
:thumbsup: this was more curiosity than anything else. One could always write his own not
anyway
I've got quite an interesting situation;
I have a spec that generates a fairly complex data structure
I sometimes want to overide some of the generators to make it use foreign keys from the database
but it only works intermittently:
I have hooked up a snitching ILookup
to tell me what is happening around
(if-let [g (c/or (when-let [gfn (c/or (get overrides (c/or (spec-name spec) spec))
(get overrides path))]
and what I see is that
When a recompile that spec directly to REPL, then the overridy will wok
When I recompile the entire namespace to REPL, it does not work
Which is intriguing behavourso this my ILookup
(reify ILookup
(valAt [_ k]
(println (str "valAt*1: " k))
(println (str "==>" (get m k)))
(get m k))
(valAt [_ k nf]
(println (str "valAt*2: " k ":" nf))
(println (str "==>" (get m k nf)))
(get m k nf))
)
when it works, I see
valAt*1: :db.generators.offers/offer
==>
valAt*1: []
==>
valAt*1: :db.generators.offers/offer_headline
==>
valAt*1: [:offer_headline]
...
when it does not work, I see
valAt*1: :db.generators.offers/offer
==>
valAt*1: []
==>
is this ringing any bells?
specs compile in their dependent specs so if you modify a spec, you need to reload any specs that depend on it. that's a likely reason you'd see different results for the two cases
I'd expect "recompile the entire namespace" to give you the more accurate answer.
hard for me to tell from this what the actual problem is though
perhaps I have misunderstood usage. I looked at
(doc spec/gen)
-------------------------
clojure.spec.alpha/gen
([spec] [spec overrides])
and hoped that I could plug in some overrides that would get me proper foreign keys, and it would just workwould you expect to re generate all of the specs to handle a overrides map?
you should be able to plug in overrides that way. I was responding to
When a recompile that spec directly to REPL, then the overridy will wok
When I recompile the entire namespace to REPL, it does not work
which seemed like a pretty textbook outcome from spec compilationyou haven't actually shown what you're doing, so I can't really comment
can you give a full example?
(spec/def ::offer
(spec/keys
:req-un [::offer_headline
::offer_classifiers
::offer_title
::offer_title_short
::offer_merchant
::offer_voucher_codes
::offer_description
::offer_terms_and_conditions
::offer_terms_and_conditions_url
::offer_claim_restrictions
::offer_presentation]
:opt-un [::offer_pre_claim_advice
::offer_key_terms
::offer_redemption_guidelines
::offer_taxable_value
::offer_identifiers
::offer_images
::offer_approval_required
::offer_discount_mechanic
::claim_condition_msisdn_list]))
(spec/def ::offer_merchant ::ingestion/merchant_id)
and then I have an ingestion namespace that says
(s/def ::merchant_id (s/and string? #(<= 1 (count %) 64)))
is the main offer specso I'm a bit suspicious about that (spec/def ::offer_merchant
and now I want to generate a bunch off offers where the merchant ids have come out of the database
so I write
(:offer_merchant
(test.gen/generate
(spec/gen :db.generators.offers/offer
(let [m {:customer.ingestion/merchant_id (constantly (test.gen/return "OVERRIDEa"))
:db.generators.offers/offer_merchant (constantly (test.gen/return "OVERRIDEb"))
:offer_merchant (constantly (test.gen/return "OVERRIDEc"))}]
(reify ILookup
(valAt [_ k]
(println (str "valAt*1: " k))
(println (str "==>" (get m k)))
(get m k))
(valAt [_ k nf]
(println (str "valAt*2: " k ":" nf))
(println (str "==>" (get m k nf)))
(get m k nf))
)))))
there is a known issue with specifying generator overrides on spec aliases
in that, it doesn't work
okay. that's a simple explanation
is there a workaround?
should I copy-and-paste it?
I think it should work if you specify it on the aliased spec :customer.ingestion/merchant_id
, but seems like you are?
I would expect OVERRIDEb and OVERRIDEc to never work here
so if I ttry to override :db.generators.offers/offer_title
instead
it still doesn'tt work though
(:offer_title (test.gen/generate
(spec/gen :db.generators.offers/offer
{:db.generators.offers/offer_title (constantly (test.gen/return "OVERRIDEb"))})))
=> "SC3T9wmvO54Q2TNx4"
`what is test.gen?
[clojure.test.check.generators :as test.gen]
so test.check.generators expects something different than clojure.spec.gen.alpha - namely, the spec version takes generator thunks, whereas I think test.check just takes generators
can you try it with spec's version?
I'm not sure what test.check supports as far as override marking - I'm not sure it even knows about the attributes as that's all spec registry based
well it looks like all logic is buried inside clojure.spec.alpha/gensub
so perhaps I don't understand what you are asking. If I run
(:offer_title (spec.gen/generate
(spec/gen :db.generators.offers/offer
{:db.generators.offers/offer_title (constantly (test.gen/return "OVERRIDEb"))})))
I get the same outcome as when ran test.gen/generate
the good news is that I'm an idiot
so what I'm doing to sabotage the overridee
The declaring file goes like this
(ns db.generators.offers
...
(defmacro add-gen
"update spec to add generator"
[k g]
`(spec/def ~k
(spec/with-gen ~k
(constantly ~g)) ))
(defmacro update-gen
[k f]
`(add-gen ~k (~f (clojure.spec.alpha/gen ~k))))
...
(spec/def ::offer
(spec/keys
:req-un [::offer_headline
...
(update-gen ::offer (fn [g] (test.gen/fmap add-msisdn-csv-bytes g)))
...
so after the offer spec is declared
I go and set its generator using with-gen
to mix in an extra bit of functionality
and the existence of this gfn
means that
clojure.spec.alpha/map-spec-impl
sees
(gen* [_ overrides path rmap]
(if gfn
(gfn)
(let [rmap (inck rmap id)
...
and says >Ooh a gfn. My work here is done
Thankyou for your help
I'm not sure how I'm going to fix it but at least I understand it
Some of this stuff really needs a rethink, itโs pretty tricky
well I thought the the next spec counts as a rethink?
discovering where the tripwires are is tricky
that's true of my entire time with Clojure
you have to trip 'em to find 'em
Yeah, we may get to it in spec 2
right its the weekend in my timezone
have a goodd weekend Alex
what is the difference between: (s/coll-of ... :kind vector?) and (s/coll-of ... :into []) ?
kind adds a validation on the input
into is about gen and conform (output)
(but kind also impacts the starting coll for gen/conform)
No, they are different purposes
what is the best :ret
spec for "predicaty" function? boolean?
is insufficient because nil is falsey. and because everything but false/nil - is truthy. any?
Depends on what the function returns
Spec the truth
(s/nilable boolean?) is useful sometimes
Donโt use sets for logically false values
function is supplied by user, and is used as predicate, but return value is not compared to true or false.
it actually will be an atom, not just something dereferable. and fn might call swap! or reset! on this arg
on the boolean question, I'd just not spec it then
not spec the ret at all
nothing to check
on atom, I'd do the first one
or actually, use IAtom, the interface, not the concrete class