Fork me on GitHub
#clojure-spec
<
2021-03-18
>
Eamonn Sullivan15:03:05

Here's a question that'll probably out me as someone who doesn't understand the concept: Is is possible to spec a "closed" map, one that has some number of required and optional keys, but no other keys?

Eamonn Sullivan15:03:23

I'm trying to translate the JSON schema concept of

additionalProperties = false

Eamonn Sullivan15:03:25

probably s/map-of #{...} will try that.

Eamonn Sullivan15:03:57

I guess that's a spec2 thing?

borkdude15:03:44

This will be supported in spec2, malli already supports it

borkdude15:03:29

you could also try to do this using a predicate, (s/and ... (fn [map] check-the-keys))

Alex Miller (Clojure team)15:03:26

the important part of our perspective in spec 2 is that we think that "closed" should be a property of the check, not a property of the spec

borkdude15:03:36

good point!

borkdude15:03:39

@U064X3EF3 Are you considering doing some journal updates again in the future? I for one really enjoyed them.

Eamonn Sullivan15:03:11

I think I see. So, I have an API that's dumb as a bag of rocks and will spit out any blob of json that includes anything other than, say, five keys. There are many combinations of those that are allowed, but it will throw a hissy fit if a key isn't one of those five.

Eamonn Sullivan16:03:09

Only one of them is required and the rest optional, which all fit nicely into (s/keys :req-un [,,,] :opt-un [,,,]) until I did a typo in one of the opt-uns.

Eamonn Sullivan16:03:20

passes the spec; fails in life.

vemv16:03:05

> should be a property of the check, not a property of the spec (FWIW!) while one can see the value of this, it's also hard to see how this would not conflict with DRY. If one tried to DRY out n identical calls as a reusable "thing", what would be that thing be? It seems to me that inherently, the 'thing' would effectively become a spec (whether it's an actual spec, or a de-facto spec: e.g. a defn)

Eamonn Sullivan17:03:31

Actually, this was a bit too easy. Must have missed something?

(s/def ::environments #{:int :test :stage :live})
(s/def ::valid-top-level-keys #{:name :description :values})
(s/def ::name string?)
(s/def ::description string?)
(s/def ::values (s/map-of ::environments string?))

(s/def ::config (s/and
                 (s/keys :req-un [::name]
                         :opt-un [::values ::description])
                 (s/map-of ::valid-top-level-keys any?)))

(s/def ::release-config (s/coll-of ::config))

(comment
  (s/valid? ::release-config [{:name "something" :description "some description"}])
  (s/valid? ::release-config [{:name "something" :desciption "some description with typo"}])
  )

Eamonn Sullivan17:03:49

I was already doing something similar with the :environments spec.