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 clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
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 clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to