Fork me on GitHub
#clojure-spec
<
2017-11-10
>
uwo19:11:01

I know this question comes up all the time, and that multi-spec is a way to solve it. but just asking again to see if there’s another sol’n: we want to ensure that a data structure at some point in our system conforms to a certain shape. The data is nested and the keys already have fixed namespaced names. It’s easy to specify required keys at the top level, but at every nested level the key already has a fully qualified name, and that fully qualified key has different requirements in different system contexts. The only two sol’ns I see atm are multi-specs and key-name rewriting. Using an empty (s/keys) is not a sol’n in this case, because we need conform to ensure that required fields at nested levels are present.

alexmiller19:11:48

when you say “fully qualified key has different requirements in different system contexts” you have started off by violating spec’s assumption

alexmiller19:11:59

unless you spec it to cover all possible contexts

uwo20:11:01

@alexmiller gotcha. I figured that was the answer. Those fully qualified key names are the ones we place in datomic, and they get shuffled all over the app. It’s just different requirements for the “same” data in different contexts

qqq20:11:11

I'm looking at:

(s/def ::ingredient (s/cat :quantity number? :unit keyword?))
(s/conform ::ingredient [2 :teaspoon])
;;=> {:quantity 2, :unit :teaspoon}
I'm starting to get the impression spec is NOT ONLY for unit testing / debugging, but also something intended to be used in live production code ?

qqq20:11:25

It's not clear to me how this s/conform is useful, unless it is meant to be used in production to 'reshape' data

taylor21:11:36

I don’t think it’s intended to arbitrarily reshape data, but the “tagged” output from conform can be useful. For example, if you’re expecting different types of inputs, s/or will tag the matched spec in the conform output, otherwise it’d be hard to tell which spec matched

taylor21:11:56

I do use it in production for similar cases, one example is a message handler that receives a few different types of messages that don’t have a natural “key”. conform output tells me exactly which kind of message it is

seancorfield21:11:54

@qqq We use spec very heavily in production code. We s/conform input values from forms and REST APIs to the desired shape/types.

seancorfield21:11:40

We define a spec for each API endpoint, and we s/conform the raw Ring :params to the spec. If it's s/invalid? then we use s/explain-data to guide us in returning error code and messages, otherwise we have a validated parameters map.

seancorfield21:11:28

We limit the coercions tho'... We do string->long, string->boolean, string->date, and very little else.

qqq23:11:13

@seancorfield: interesting, I was thinking that something vaguely along those lines would be possible ; nice to see someone do it in practice

qqq23:11:32

unrelated spec question: how does s/or and s/alt differ ?

qqq23:11:07

I'm running them with s/conform ... and getting identital results

seancorfield23:11:10

s/alt is for alternatives in a (sequence) regex; s/or is for specific alternatives in a spec.

seancorfield23:11:46

(so s/alt mostly goes with s/cat etc)

qqq23:11:06

Is there a way to define a spec, then selectively, on valid? calls turn "recursion on / recursion off" ? Practically, imagine we are speccing SVG. For a given SVG node, there are (1) properties of this node and (2) the children of this node. I want to be able to define the spec once, and then selectively say: 1. check that this node itself is valid (but don't care much about its children) vs 2. check that this node itself is valid, and that all its children (and all descendants) are valid too so one is a "shallow, just this level check" another is a "deep, check the entire tree check"

bfabry23:11:08

I'm 99.4% sure that the answer is "define 2 specs"