clojure-spec

About: http://clojure.org/about/spec Guide: http://clojure.org/guides/spec API: https://clojure.github.io/spec.alpha/clojure.spec.alpha-api.html
2020-04-11T13:46:10.079700Z

Hi! This is definitely a novice question, but I’ve struggled for a day or so trying to figure out the best way to use spec in my re-frame application. My problems stem from not grokking spec I think.

2020-04-11T13:46:48.080500Z

I’m using my app db to store form information under a :form key. Forms are stored by form-id.

2020-04-11T13:48:13.080600Z

2020-04-11T13:52:21.082400Z

In each field the “error” is set based on a spec that gets checked on each edit:

(s/def ::id (s/and string? #(> (count %) 0)) )
(s/def ::team (s/and string? #(> (count %) 0)) )
(s/def ::nickname (s/and string? #(> (count %) 0)))
(s/def ::password (s/and string? #(> (count %) 7)) )

2020-04-11T13:52:35.082800Z

(Lengths are set to zero but will change)

2020-04-11T13:54:28.084600Z

But I also want to define a spec for the form as a whole, so I can pass the “signup” form above to (s/valid?). But I can’t figure out how to define the structure above, where signup has four keys, each of which have the same structure

2020-04-11T13:55:30.085900Z

in :signup :team for example, I want to say signup has a map that requires a :team key, and the team key is a map that has a :value key that is defined by ::team in the snippet pasted above

2020-04-11T13:56:37.086700Z

I can’t define a ::value spec to include in (s/keys) for :team - because what :value is is different for each sibling key in :signup

shooit 2020-04-11T16:22:44.089900Z

I’m not sure that I understand 100% but you might need to put each :value key into a different namespace so that they can be a distinct spec e.g :foo/value, :bar/value. You could also check out multi-spec for multimethod like dispatch on the submaps

alexmiller 2020-04-11T16:26:39.091100Z

spec is primarily designed to associate semantic specs with namespaced keys, so by using :value in each of these places (instead of :id/value or whatever), you are outside the sweet spot

alexmiller 2020-04-11T16:27:21.091700Z

you can do this with s/keys and :req-un for unqualified keys though, but you'll need to match up the name parts of the specs

alexmiller 2020-04-11T16:32:19.095100Z

(s/def :team/value ::team)  ;; same for the others
(s/def :team/team (s/keys :req-un [::team/value])) ;; same for the others
(s/def ::signup (s/keys :req-un [:team/team ..etc..]))

alexmiller 2020-04-11T16:33:23.096Z

as I said, this is outside the expected pattern so it's more work for sure

2020-04-11T18:08:18.097100Z

Thanks Alex. What is the preferred way to satisfy the goal I have here, which is to independently validate the form contents and the form data as a whole? Is the correct answer to my confusion “read more”? 🙂

alexmiller 2020-04-11T19:17:40.098Z

the preferred way is to have namespaced attributes with one meaning