Fork me on GitHub
#clojure-spec
<
2020-08-10
>
johanatan02:08:06

@seancorfield i'm not sure how to force s/check to use my conformer but yes that would seem to work if it were possible to do so.

johanatan02:08:40

i wonder if there is a "conformer registry" i could write into ?

seancorfield02:08:04

No, I mean directly in your Spec. So it always runs when a Spec is conformed.

johanatan02:08:37

suppose i have a spec defined like so:

(s/def ::an-entity
  (s/with-gen
    ::a-base-entity
    #(gen/let [...] (with-meta a-base-entity-conforming-structure {:some :metadata}))))
are you suggesting to use s/and to weave in a conformer like so: (s/and (s/conformer ...) ::a-base-entity) ?? or something else?

johanatan02:08:41

the base entity in this case looks like this:

(s/def ::re-frame-event
  (s/and vector?
         (s/cat :event-name qualified-keyword?
                :params (s/* any?))))

johanatan02:08:35

actually i can also just share the derived event:

(s/def ::callback-func-event
  (s/with-gen
    ::re-frame-event
    #(gen/let [params (s/gen (s/coll-of any?))
               triggers-failure? (gen/frequency [[1 (gen/return true)] [9 (gen/return false)]])
               event-name (s/gen qualified-keyword?)]
       (with-meta
         (concatv [event-name] params)
         {:triggers-failure? triggers-failure?}))))

seancorfield02:08:49

Put s/conformer in the second/last slot of s/and

seancorfield02:08:25

That allows you to modify the (conformed) value as part of the conform process without affecting the validation.

seancorfield02:08:54

(s/and ... (s/conformer #(with-meta % {:what :ever}))) -- untested but that's what I had in mind.

johanatan03:08:28

hmm, ok. let me try that

johanatan03:08:23

the problem with that is that the meta depends on a generated value

johanatan03:08:37

and i need to somehow "attach" that value to the generated structure

johanatan03:08:50

while allowing it to survive the first "conform" in the s/and

johanatan03:08:41

i.e., the following doesn't work because ::re-frame-event has already stripped away the meta and destructured into labeled data

johanatan03:08:43

(s/def ::callback-func-event
  (s/with-gen
    (s/and ::re-frame-event (s/conformer identity))
    #(gen/let [params (s/gen (s/coll-of any?))
               triggers-failure? (gen/frequency [[1 (gen/return true)] [9 (gen/return false)]])
               event-name (s/gen qualified-keyword?)]
       (with-meta
         (concatv [event-name] params)
         {:triggers-failure? triggers-failure?}))))

johanatan03:08:40

i could "cheat" and stuff it into the last "param" and allow my conform to strip it out of there and put it in the meta

johanatan03:08:52

but that kind of violates a sense of purity 🙂

johanatan03:08:50

and would also only work for some specific subset of cases; i.e., we're lucky in the sense that params is an s/* of s/any which could accommodate the data

johanatan03:08:50

perhaps i just make the single conformer the "spec" and forget about using s/and ?

johanatan03:08:03

then do the base conform within that conform

seancorfield03:08:15

True, you could have a completely custom "predicate" that conforms as a spec...

johanatan03:08:45

yea, i think i'll go that route. probably the path of least resistance at this point

johanatan03:08:01

this works:

(defn metadata-preserving [spec]
  (s/conformer
   (fn [v]
     (let [m (meta v)
           res (s/conform spec v)]
       (if (= res ::s/invalid)
         res
         (with-meta res m))))))

johanatan03:08:59

used like so:

(s/def ::a-spec
  (with-gen
     (metadata-preserving ::a-base-spec)
     #(with-meta ( ... generation here ) {:some :meta})))