While it doesn't use spec, you can do this using the validate-map-keys helper function in the Tupelo library <https://github.com/cloojure/tupelo>. It verify that a map does not contain any keys other than those you specify. The unit test shows it in action:
(ns tst.demo.core (:use demo.core tupelo.test ) (:require [tupelo.core :as t] )) (t/refer-tupelo) (dotest (let [map-ab {:a 1 :b 2} map-abc {:a 1 :b 2 :c 3}] (is= map-ab (validate-map-keys map-ab [:a :b])) (is= map-ab (validate-map-keys map-ab [:a :b :x])) (is= map-ab (validate-map-keys map-ab #{:a :b})) (is= map-ab (validate-map-keys map-ab #{:a :b :x})) (is= map-abc (validate-map-keys map-abc [:a :b :c :x])) (is (thrown? IllegalArgumentException (validate-map-keys map-ab [:a]))) (is (thrown? IllegalArgumentException (validate-map-keys map-ab [:b]))) (is (thrown? IllegalArgumentException (validate-map-keys map-ab [:a :x]))) (is (thrown? IllegalArgumentException (validate-map-keys map-abc [:a :b]))) (is (thrown? IllegalArgumentException (validate-map-keys map-abc [:a :c :x]))))) The API docs are hosted on Github Pages, but it seems to be down at the moment. You can find some older API docs (May 2017) on CrossClj: https://crossclj.info/doc/tupelo/0.9.1/index.html Alan On Tue, Oct 3, 2017 at 2:37 PM, Didier <didi...@gmail.com> wrote: > I'm loving Spec as an FYI, and I'll use it even if it stays as is, even in > its alpha state it is a great tool. I also highly value the Clojure core > value to avoid breakage, as the cost to enterprise of every migration is > very high. > > I'm just wanting to give more data points to the Core team that I hope > would be taken into consideration in the decisions of where to put the line > on what Spec provides and doesn't. > > In my case, Spec has already not gone where my code base has. I'm sure > there's use cases out there where a closed map spec would have been > problematic, so I'm glad spec has an open map spec, but I've never had this > problem, and I've failed to see anyone describe it to me where I felt the > problem was real. What I do know is that I've experienced the problem of an > open map spec that others have clearly described in this thread. When I > need to validate for security that no extra keys were added to my maps, and > when I want to be sure, for correctness, that my instrumentation is not > accidentally not asserting my input because of a missing key spec, or if I > want to be reminded that I forgot to update the spec when I extended the > map to have additional keys, then clojure.spec falls short. > > I wouldn't ask of this to be taken into account by the core spec team, if > it was easy to extend Spec to support this, but from what I could tell from > the source, it is non trivial for me to add my own strict s/keys, and it > doesn't seem possible to easily extend s/keys itself with more features. > Especially if I want all the features of s/keys, except with strict key > spec validation. > > I trust the core team to have good judgement, as they've shown to have > with much of Clojure till date, but I think this is valuable data points > for them to discuss and decide what's best. This was also part of a real > enterprise project, that took 4 months with 5 engineers to develop. We're > using it in production, so this is not feedback out of a toy project, or > just experimentation. > > Maybe there's a better way to solve this problem then a strict spec, maybe > we need a strict s/validate?, or some other more powerful and flexible > construct. And maybe there are easy to extend spec for my needs, then I'd > love to learn about those. > > Anyways, for the purpose of gathering data points about this use case. It > currently seems like Me, Yury, Leon, Puzzler, and Tommi have all faced this > issue. I've fixed it by doing my own validation on top of spec. So I only > use spec as a data description language, and not as a validation tool. I > use s/describe to get the keys, and then I resolve the specs, and assert > that no more keys are on the map then what I got from s/describe. The > downside is that I can't use instrumentation, assert, validate?, etc. So > its hard to guarantee that the spec is always validated as my data should. > The spec data description language is not powerful enough to be able to > model invariants such as a closed set of keys, or keys which must have > associated specs, unless I implement that predicate spec myself, which I > couldn't figure out how to do. > > Since spec is still alpha, I think this is a great time to bring up these > issues. > > > On Tuesday, 3 October 2017 13:29:40 UTC-7, Tommi Reiman wrote: >> >> Open Specs seem like a good idea. >> >> In real life, we need to close our Data at system borders. >> >> With Java, there is Jackson, which fails by default on extra keys. The >> Schema, the models are closed by default. JSON Schemas can be closed. >> >> Saying "use normal clojure" on top of Spec mean double-validation, which >> is both error-prone and boilerplate. >> >> Open/Closed is a great principle. >> >> Spec should be just more Open to allow people (or 3rd party libs) to >> extend it to support "close my (open) specs" at runtime = coercion. >> >> If Runtime Transformations stay out of scope, It would be good to have a >> guide on how to write Spec + (beautiful) clojure transformations for >> web/ring apps. There might be tricks we just haven't thought of. >> >> cheers, >> >> Tommi >> >> tiistai 3. lokakuuta 2017 20.57.34 UTC+3 Alex Miller kirjoitti: >>> >>> >>> >>> On Tuesday, October 3, 2017 at 12:25:40 PM UTC-5, Didier wrote: >>>> >>>> | Spec-tools (https://github.com/metosin/spec-tools) has some tools >>>> for this: the spec visitor (walking over all core specs, e.g. to collect >>>> all registered specs) and map-conformers: fail-on-extra-keys and >>>> strip-extra-keys. >>>> >>>> I understand the core team wanting to take a minimal approach to spec, >>>> and that open is easier to restrict later. >>>> >>> >>> It's not about easier, it's about possible. Open grows, closed breaks. >>> >>> >>>> But I worry that already in alpha state, spec is unpractical for many >>>> people as is >>>> >>> >>> This is demonstrably false. Many people are using it and happy with it >>> now. >>> >>> >>>> , and Orchestra and Spec-tools are already needed supplement. >>>> >>>> For instrumentation, it's no big deal, but for specs I think it is. >>>> Having a canonical set of specs accross Clojure shops is a way to form a >>>> common language. If I start having my custom map specs, and so does >>>> everyone, I'd be tempted to say something core is missing. Strict map specs >>>> which also vallidates each key has a registerer spec I think is a glaring >>>> omission. >>>> >>> >>> I'm not sure how to say this other than that we are playing a longer >>> game than you, looking out into the future to other potential functionality >>> and where code bases are likely to go over time. >>> >>> We have already seen a number of cases where strict map specs broke >>> during evolution both inside and outside Cognitect projects as people added >>> keys. Again, nothing prevents you from both validating your spec AND >>> applying whatever additional validation you wish to perform. But if you put >>> the strict check into the spec, you have poured concrete on the spec and it >>> is dead. >>> >>> >>>> Having used spec in one of my design, I've had to justify already to 6 >>>> people, some being senior security engineers, why the validation allows for >>>> open keys. >>>> >>> >>> Those other people are right - you should check that if it's important. >>> But it doesn't have to be part of the spec. Specs should say what must be >>> true (open), not what MUSTN'T be true (closed). This is in some ways >>> another variant of the open/closed principle (the O in SOLID). >>> >>> >>>> Other team members were just confused as to why that was, and the only >>>> argument I had was that Rich doesn't like to break APIs :p. >>>> >>> >>> This is an exceptionally good argument. Why would you dismiss it? >>> >>> >>>> But I've had to add validation on top to pass security audit. >>>> >>> >>> Good! >>> >>> >>>> So I think while not breaking APIs when incrementally adding specs to >>>> legacy code is a good use case, there's the security and safety use case >>>> which seems to be shared by a large swat of Clijurist, and I think spec is >>>> in need of a core support for it. >>>> >>> >>> No, it doesn't! Spec doesn't have to do everything. We've got this whole >>> beautiful language to use. >>> >>> >> -- > 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. > -- 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.