Whoops, that should be:
(defmacro only-keys
[& {:keys [req req-un opt opt-un] :as args}]
`(s/merge (s/keys ~@(apply concat (vec args)))
(s/map-of ~(set (concat req
(map (comp keyword name) req-un)
opt
(map (comp keyword name) opt-un)))
any?)))
On Sunday, 25 September 2016 17:13:12 UTC+10, Alistair Roche wrote:
>
> Yeah, my team and I were initially surprised at the lack of a built-in
> option for this to s/keys, but TBH it's been an unusual use case so far,
> and Alex / Beau's solutions don't seem particularly onerous despite the
> repetition.
>
> I suppose if you're using it all over the place you could write a macro
> like this:
>
>
> (defmacro only-keys
> [& {:keys [req req-un opt opt-un] :as args}]
> `(s/and (s/keys ~@(apply concat (vec args)))
> (s/map-of ~(set (concat req
> (map (comp keyword name) req-un)
> opt
> (map (comp keyword name) opt-un)))
> any?)))
>
>
>
> (please feel free to suggest a neater way!)
>
> Cheers,
>
> On Wednesday, 21 September 2016 18:08:25 UTC+10, David Goldfarb wrote:
>>
>> Nice, thanks. I had not thought to use map-of for this. And, s/merge
>> certainly helps too.
>>
>> The only remaining issue for me is that this requires supplying the list
>> of keys twice.
>> AI think this case is general enough that it is worth extending the
>> s/keys macro to support: (s/keys :req [::a ::b] :allow-other-keys false)
>>
>> Or, if is is objectionable to have a keyword default to true when not
>> supplied, perhaps: (s/keys :req [::a ::b] :strict-keys true)
>>
>> On Tuesday, September 20, 2016 at 9:47:43 PM UTC+3, Alex Miller wrote:
>>>
>>> For stuff like this s/merge is probably preferable to s/and (when
>>> combining map specs) - the difference being that merge does not flow
>>> conformed results, will combine all failures, and that gen can work better
>>> in some cases.
>>>
>>> (s/def ::a int?)
>>> (s/def ::b string?) ;; changed for this example
>>> (s/explain ::my-map {::a 1 ::b 2 ::BAD 3})
>>> In: [:user/b] val: 2 fails spec: :user/b at: [:user/b] predicate: string?
>>>
>>> ;; vs:
>>>
>>> (s/def ::my-map2 (s/merge (s/keys :req [::a ::b]) (s/map-of #{::a ::b}
>>> any?)))
>>> (s/explain ::my-map2 {::a 1 ::b 2 ::BAD 3})
>>> In: [:user/b] val: 2 fails spec: :user/b at: [:user/b] predicate: string?
>>> In: [:user/BAD 0] val: :user/BAD fails spec: :user/my-map2 at: [0]
>>> predicate: #{:user/a :user/b}
>>>
>>> ^^ Note you get *both* failures here - both bad attribute value AND the
>>> invalid key vs the prior one where you only get the first failure.
>>>
>>>
>>> On Tuesday, September 20, 2016 at 11:38:47 AM UTC-5, Beau Fabry wrote:
>>>>
>>>> boot.user=> (s/def ::my-map (s/and (s/keys :req [::a ::b]) (s/map-of
>>>> #{::a ::b} any?)))
>>>> boot.user=> (s/explain ::my-map {::a 1 ::b 2 ::BAD 3})
>>>> In: [:boot.user/BAD 0] val: :boot.user/BAD fails spec:
>>>> :boot.user/my-map at: [0] predicate: #{:boot.user/a :boot.user/b}
>>>>
>>>> Seems better
>>>>
>>>> On Tuesday, September 20, 2016 at 5:38:10 AM UTC-7, David Goldfarb
>>>> wrote:
>>>>>
>>>>> In clojure.spec, how can I declare a map that accepts only certain
>>>>> keys?
>>>>>
>>>>> *{::a 1 ::b 2 ::BAD 3}* does conform to *(s/keys :req :req [::a ::b])*,
>>>>> but I want a spec that will be bothered by ::BAD or any other undeclared
>>>>> key.
>>>>>
>>>>> My use case: I am introducing spec to some legacy code, and I want to
>>>>> be warned if I have failed to specify some elements that may appear in my
>>>>> map.
>>>>>
>>>>>
>>>>> Question 2:
>>>>>
>>>>> So, assuming that this is not possible currently, I brute-forced it
>>>>> with:
>>>>>
>>>>> *(defn- key-checker [valid-keys]*
>>>>> * (fn [map-to-check]*
>>>>> * (empty? (clojure.set/difference (into #{} (keys map-to-check))
>>>>> valid-keys))))*
>>>>>
>>>>> *(s/def ::my-map (s/and (s/keys :req [::a ::b]) (key-checker #{::a
>>>>> ::b})))*
>>>>>
>>>>>
>>>>> Ignoring the ugly, and easily fixable, smell of the duplicated set of
>>>>> keys, this has a bigger problem:
>>>>>
>>>>> If the predicate fails, the error that assert gives me is *"{... big
>>>>> ugly map ...} fails predicate: (key-checker #{::a ::b})"* with no
>>>>> easy way for the viewer to see which key failed. Can I somehow hook into
>>>>> the explain mechanism to give a more useful message?
>>>>>
>>>>
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.