Fork me on GitHub
#clojure-spec
<
2024-03-08
>
hlship18:03:44

I'm working on retroactively defining specs for Pedestal routes; mostly it's worked well, but I've hit my limit trying to spec a table route. A table route looks like ["/" :get my-handler :route-name ::my-handler] The first three parts are straight forward, bit there are these clauses (:route-name <keyword>) and (:constraints <map>) that can optionally follow the fixed part of the table route, in any order. My initial pass:

(s/def ::table-route
  (s/cat
    :path ::path
    :verb keyword?                                          ;; This should be constrained based on options
    :handler ::table-handler
    :clauses (s/* ::table-route-clause)))

(s/def ::table-handler
  (s/or
    :symbol symbol?
    :interceptor ::i/interceptor
    :interceptors ::i/interceptors))

(s/def ::table-route-clause
  (s/or
    :route-name ::table-route-name-clause
    :interceptors ::table-interceptors-clause))

(s/def ::table-route-name-clause
  (s/cat :k #{:route-name}
         :route-name ::route-name))

(s/def ::table-interceptors-clause
  (s/cat :k  #{:interceptors}
         :interceptors ::i/interceptors))
But that isn't working, and I get:t
#{[... ... ... :route-name ...]
                 ^^^^^^^^^^^
    ["/users/:id"
     :get
     [io.pedestal.http.route-test/view-user]
     :route-name
     :io.pedestal.http.route-test/view-user
     :constraints
     {:id #"\d+"}]}

should satisfy

  (fn
   [%]
   (or (nil? %) (sequential? %)))
Any pointers here?

hlship18:03:57

I think I found it, s/keys*.

hlship18:03:08

(s/def ::table-route
  (s/cat
    :path ::path
    :verb keyword?                                          ;; This should be constrained based on options
    :handler ::table-handler
    :clauses  (s/keys* :opt-un [::route-name ::constraints])))


(s/conform ::specs/table-route
             ["/users/:id" :get [`view-user] :route-name ::view-user :constraints {:id #"\d+"}])
=>
{:path "/users/:id",
 :verb :get,
 :handler [:interceptors [io.pedestal.http.route-test/view-user]],
 :clauses {:route-name :io.pedestal.http.route-test/view-user, :constraints {:id #"\d+"}}}

duckie 1
hlship18:03:06

Thanks for being my rubber duck!

Alex Miller (Clojure team)19:03:15

I think maybe s/alt instead of s/or may also have been a better path

Alex Miller (Clojure team)19:03:42

keys* is like "& args" - it allows any number at the end, not sure if that's what you want

hlship22:03:28

Perhaps you can look at the Pedestal PR when it's ready? I'm kind of in the camp of "it works, all tests pass". I actually like the idea that there can be extra options beyond :route-name and :constraints, so I may integrate that and document it.