This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-09-26
Channels
- # 100-days-of-code (3)
- # announcements (2)
- # beginners (237)
- # bitcoin (2)
- # boot (5)
- # cider (15)
- # cljs-dev (9)
- # cljsrn (6)
- # clojure (75)
- # clojure-estonia (1)
- # clojure-italy (8)
- # clojure-losangeles (1)
- # clojure-nl (1)
- # clojure-spec (68)
- # clojure-uk (80)
- # clojurescript (89)
- # cursive (31)
- # datomic (22)
- # emacs (2)
- # events (3)
- # figwheel-main (184)
- # fulcro (28)
- # graphql (1)
- # hyperfiddle (2)
- # jobs (1)
- # jobs-discuss (64)
- # luminus (5)
- # off-topic (16)
- # om (2)
- # onyx (1)
- # pedestal (12)
- # portkey (1)
- # re-frame (13)
- # reagent (56)
- # reitit (13)
- # ring-swagger (13)
- # shadow-cljs (145)
- # slack-help (2)
- # specter (6)
question : I’ve heard about context specific specs quite a few times i.e. same entity but different uses e.g. reading vs inserting a map from/to db. another might be reading entities in a list vs individually. I can guess how these specs might look but was hoping I could learn from looking at others. does anyone know of public codebases with examples of this technique?
@steveb8n I would be interested to see this too.
But what you describe feels like something spec is not meant to do. Spec specs data, not entities or identities. ::entity-while-reading
, ::entity-while-inserting
might be better as two different specs. Maybe metosin/spec-tools
can do this.
Yeh spec doesn’t support this, not sure where you heard about that. If you find out I’m interested
There are two cases that hurt where I want this. One is dealing with datomic data. Because it’s a graph each ref key could be (in the widest possible case) an entity id, a string, a lookup ref, or a nested vector or map, again with unknown keys ( depends on what was pulled)
At certain spots in the program I want to allow only a few of those possibilities. Eg On read I often want to guarantee that a certain set of sub-keys was pulled
The other case is dealing with data where the same key is used but it may have a narrower spec depending on the “kind” of map it is in
Eg a key could have values 1 2 or 3, but that key must have value 1 when some other key has a certain value
Multi-spec and predicates can do this but it’s awkward and verbose and doesn’t gen well
Interested about this, I would like to be able to express that a restriction on a composed spec. If spec1 has value :a
within a set of alternatives, spec2 cannot have that value (both of spec1 and spec2 are keys of another spec)
To do this, one can s/and
an arbitrary function at the compound spec level to make this happen defined as the second operand (for gen
support to be there)
In my case I don’t want to merge two sets of specs. I have a spec that is the composition of 5 specs (with s/keys
) and I wanted two ensure that two of those did not have the same value
This works with gen because the set of possibilities for those specs is 2 😛 (a really small set)
(s/def ::integration (s/and
(s/keys :req [:m/name
:m/doc-type
:m/origin
:m/target
:m/rules])
distinct-origin-target))
what do you run into?
(I can see how distinct-origin-target
can bump gen
if the resulting restriction is too big a set)
something like if :m/doc-type is "x", :m/origin must be some narrower range of values that is normally allowed
Right, if m/origin
for example is restricted to a given string
matching a regex it will destroy gen
’s ability to generate example data
not sure how with-gen
would work with that compound spec though
(that’s how I make sure gen
continues to work)
this is really just contravariance/covariance at the end of the day. The trouble is spec doesn't have a way to structurally use the same map key and spec that key two different ways
I wrote a hacky keys+ macro to try and do this. It allows an inline redef of a key’s spec+generator, but it will still conform both the “natural” and the redef-ed spec of the key
In my reagent app on cljs, I use a spec validator on my app’s state atom. This works well, but is unfortunately a bit slow in some cases. Is there anything clever one can do to do validations in this way but skip parts of the state tree that haven’t changed? The answer might be just to use more than one atom.
the validation function receives the two states as args, so at least hypothetically you could check only changed subtrees using cheap identity checks
I'd imagine a classic recursive tree walk, either short-circuiting if identity is true, or checking if identity was false, for each branch new/old
then again you'd have to also walk subtrees of the spec to really make it work...
sounds messy
Is the state atom flat or deep? If its flat you could have a spec for each of the main subtrees, and only validate the main subtrees that have changed?
that is probably the best plan
yea that’s probably the right approach. actually i already have specs for each of the main subtrees
and I bet using identical?
instead of =
performs better for checking each subtree for changes, but that should be straightforward to swap out and test
that works beautifully. the obvious thing i didn’t realize is that the validator of course is called before the atom is changed so you can just compare it against the old atom
I would like to be able to express that a restriction on a composed spec. If spec1 has value :a
within a set of alternatives, spec2 cannot have that value (both of spec1 and spec2 are keys of another spec’d map)
Is there some way to achieve this?
@lee.justin.m one of the args to the validator function is the old state, one is the new state
assuming the state tree is a map, and if there are no cross-keys validations (if k1 is this, k2 should be that), you can top level diff new and old values, and just test it against empty (s/keys) @lee.justin.m
might save you some milliseconds, if state is huge, but diff is just few keys at a time
@noisesmith I was using https://clojuredocs.org/clojure.core/set-validator! which says `validator-fn must be nil or a side-effect-free fn of one argument, which will be passed the intended new state on any state change`
@misha yea there aren’t that many top level keys and most of them won’t change, so I think I can just test them with identical?
that's the validator, yes. I don't know about other ones either. I think @noisesmith meant usual ref-callback with [key ref old new] signature
@dadair yeah that's the one I was thinking of
I got the two functionalities confused
the only thing I’d really like to do is have the validator throw an error synchronously so that i get a stack trace from the offending function, though this is straying from the channel topic
are specs queryable once they’ve been made? it’d be cool to ask for the spec corresponding to a key in a map dynamically
https://clojure.github.io/spec.alpha/clojure.spec.alpha-api.html#clojure.spec.alpha/get-spec
then s/form
For example, this works fine:
(s/conform (s/or :success (s/keys :req [::email/email-address])
:not-found nil?) {::email/email-address ""})
but if I have additional keys in the value, i get invalid:
(s/conform (s/or :success (s/keys :req [::email/email-address])
:not-found nil?) {::email/email-address ""
::email/creation-timestamp "2018-09-26T21:47:57.304Z"})
@chris547 If you call s/explain
(instead of s/conform
) in that second case, what does it say? I suspect you have a spec for ::email/creation-timestamp
and your string is not valid for that.
In: [:io.cloudrepo.signup.email/creation-timestamp] val: "2018-09-26T21:47:57.304Z" fails spec: :io.cloudrepo.signup.email/creation-timestamp at: [:success :io.cloudrepo.signup.email/creation-timestamp] predicate: :clojure.spec.alpha/unknown
val: #:io.cloudrepo.signup.email{:email-address "", :creation-timestamp "2018-09-26T21:47:57.304Z"} fails at: [:not-found] predicate: nil?